Skip to content
This repository was archived by the owner on Oct 16, 2025. It is now read-only.

Commit 43d77b1

Browse files
authored
Merge pull request #34 from pamelafox/port-openai
Port to OpenAI vs Azure OpenAI, port infra
2 parents 8da5c5e + 2bbbe50 commit 43d77b1

File tree

68 files changed

+188286
-243736
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+188286
-243736
lines changed

.env.sample

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
# API_HOST can be either azure, ollama, openai, or github:
22
API_HOST=azure
33
# Needed for Azure:
4-
AZURE_OPENAI_ENDPOINT=https://YOUR-AZURE-OPENAI-SERVICE-NAME.openai.azure.com
5-
AZURE_OPENAI_DEPLOYMENT=YOUR-AZURE-DEPLOYMENT-NAME
6-
AZURE_OPENAI_VERSION=2024-03-01-preview
4+
AZURE_OPENAI_ENDPOINT=https://YOUR-AZURE-OPENAI-SERVICE-NAME.openai.azure.com/openai/v1
5+
AZURE_OPENAI_CHAT_DEPLOYMENT=YOUR-AZURE-DEPLOYMENT-NAME
76
# Needed for Ollama:
87
OLLAMA_ENDPOINT=http://localhost:11434/v1
98
OLLAMA_MODEL=llama3.1

.env.sample.azure

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# See .env.sample for all options
22
API_HOST=azure
3-
AZURE_OPENAI_ENDPOINT=https://YOUR-AZURE-OPENAI-SERVICE-NAME.openai.azure.com
4-
AZURE_OPENAI_DEPLOYMENT=YOUR-AZURE-DEPLOYMENT-NAME
5-
AZURE_OPENAI_VERSION=2024-03-01-preview
3+
AZURE_OPENAI_ENDPOINT=https://YOUR-AZURE-OPENAI-SERVICE-NAME.openai.azure.com/openai/v1
4+
AZURE_OPENAI_CHAT_DEPLOYMENT=YOUR-AZURE-DEPLOYMENT-NAME

README.md

Lines changed: 108 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,25 @@
22

33
This repository contains a collection of Python scripts that demonstrate how to use the OpenAI API to generate chat completions.
44

5-
## OpenAI package
6-
7-
These scripts use the OpenAI package to demonstrate how to use the OpenAI API.
5+
* [Examples](#examples)
6+
* [OpenAI Chat Completions](#openai-chat-completions)
7+
* [Popular LLM libraries](#popular-llm-libraries)
8+
* [Function calling](#function-calling)
9+
* [Structured outputs](#structured-outputs)
10+
* [Retrieval-Augmented Generation (RAG)](#retrieval-augmented-generation-rag)
11+
* [Setting up the Python environment](#setting-up-the-python-environment)
12+
* [Configuring the OpenAI environment variables](#configuring-the-openai-environment-variables)
13+
* [Using GitHub Models](#using-github-models)
14+
* [Using Azure OpenAI models](#using-azure-openai-models)
15+
* [Using OpenAI.com models](#using-openaicom-models)
16+
* [Using Ollama models](#using-ollama-models)
17+
* [Resources](#resources)
18+
19+
## Examples
20+
21+
### OpenAI Chat Completions
22+
23+
These scripts use the openai Python package to demonstrate how to use the OpenAI Chat Completions API.
824
In increasing order of complexity, the scripts are:
925

1026
1. [`chat.py`](./chat.py): A simple script that demonstrates how to use the OpenAI API to generate chat completions.
@@ -17,15 +33,22 @@ Plus these scripts to demonstrate additional features:
1733
* [`chat_safety.py`](./chat_safety.py): The simple script with exception handling for Azure AI Content Safety filter errors.
1834
* [`chat_async.py`](./chat_async.py): Uses the async clients to make asynchronous calls, including an example of sending off multiple requests at once using `asyncio.gather`.
1935

20-
## Popular LLM libraries
36+
### Function calling
37+
38+
These scripts demonstrate using the Chat Completions API "tools" (a.k.a. function calling) feature, which lets the model decide when to call developer-defined functions and return structured arguments instead of (or before) a natural language answer.
39+
40+
In all of these examples, a list of functions is declared in the `tools` parameter. The model may respond with `message.tool_calls` containing one or more tool calls. Each tool call includes the function `name` and a JSON string of `arguments` that match the declared schema. Your application is responsible for: (1) detecting tool calls, (2) executing the corresponding local / external logic, and (3) (optionally) sending the tool result back to the model for a final answer.
2141

22-
These scripts use popular LLM libraries to demonstrate how to use the OpenAI API with them:
42+
Scripts (in increasing order of capability):
2343

24-
* [`chat_langchain.py`](./chat_langchain.py): Uses the Langchain package to generate chat completions. [Learn more from Langchain docs](https://python.langchain.com/docs/get_started/quickstart)
25-
* [`chat_llamaindex.py`](./chat_llamaindex.py): Uses the LlamaIndex package to generate chat completions. [Learn more from LlamaIndex docs](https://docs.llamaindex.ai/en/stable/)
26-
* [`chat_pydanticai.py`](./chat_pydanticai.py): Uses the PydanticAI package to generate chat completions. [Learn more from PydanticAI docs](https://ai.pydantic.dev/)
44+
1. [`function_calling_basic.py`](./function_calling_basic.py): Declares a single `lookup_weather` function and prompts the model. It prints the tool call (if any) or falls back to the model's normal content. No actual function execution occurs.
45+
2. [`function_calling_call.py`](./function_calling_call.py): Executes the `lookup_weather` function if the model requests it by parsing the returned arguments JSON and calling the local Python function.
46+
3. [`function_calling_extended.py`](./function_calling_extended.py): Shows a full round‑trip: after executing the function, it appends a `tool` role message containing the function result and asks the model again so it can incorporate real data into a final user-facing response.
47+
4. [`function_calling_multiple.py`](./function_calling_multiple.py): Exposes multiple functions (`lookup_weather`, `lookup_movies`) so you can see how the model chooses among them and how multiple tool calls could be returned.
2748

28-
## Retrieval-Augmented Generation (RAG)
49+
You must use a model that supports function calling (such as the defaults `gpt-4o`, `gpt-4o-mini`, etc.). Some local or older models may not support the `tools` parameter.
50+
51+
### Retrieval-Augmented Generation (RAG)
2952

3053
These scripts demonstrate how to use the OpenAI API for Retrieval-Augmented Generation (RAG) tasks, where the model retrieves relevant information from a source and uses it to generate a response.
3154

@@ -44,7 +67,7 @@ Then run the scripts (in order of increasing complexity):
4467
* [`rag_documents_flow.py`](./rag_pdfs.py): A RAG flow that retrieves matching results from the local JSON file created by `rag_documents_ingestion.py`.
4568
* [`rag_documents_hybrid.py`](./rag_documents_hybrid.py): A RAG flow that implements a hybrid retrieval with both vector and keyword search, merging with Reciprocal Rank Fusion (RRF), and semantic re-ranking with a cross-encoder model.
4669

47-
## Structured outputs with OpenAI
70+
## Structured outputs
4871

4972
These scripts demonstrate how to use the OpenAI API to generate structured responses using Pydantic data models:
5073

@@ -54,7 +77,7 @@ These scripts demonstrate how to use the OpenAI API to generate structured respo
5477
* [`structured_outputs_function_calling.py`](./structured_outputs_function_calling.py): Demonstrates how to use functions defined with Pydantic for automatic function calling based on user queries.
5578
* [`structured_outputs_nested.py`](./structured_outputs_nested.py): Uses nested Pydantic models to handle more complex structured responses, such as events with participants having multiple attributes.
5679

57-
## Setting up the environment
80+
## Setting up the Python environment
5881

5982
If you open this up in a Dev Container or GitHub Codespaces, everything will be setup for you.
6083
If not, follow these steps:
@@ -70,61 +93,106 @@ python -m pip install -r requirements.txt
7093
## Configuring the OpenAI environment variables
7194

7295
These scripts can be run with Azure OpenAI account, OpenAI.com, local Ollama server, or GitHub models,
73-
depending on the environment variables you set.
96+
depending on the environment variables you set. All the scripts reference the environment variables from a `.env` file, and an example `.env.sample` file is provided. Host-specific instructions are below.
7497

75-
1. Copy the `.env.sample` file to a new file called `.env`:
98+
## Using GitHub Models
7699

77-
```bash
78-
cp .env.sample .env
100+
If you open this repository in GitHub Codespaces, you can run the scripts for free using GitHub Models without any additional steps, as your `GITHUB_TOKEN` is already configured in the Codespaces environment.
101+
102+
If you want to run the scripts locally, you need to set up the `GITHUB_TOKEN` environment variable with a GitHub [personal access token (PAT)](https://github.com/settings/tokens). You can create a PAT by following these steps:
103+
104+
1. Go to your GitHub account settings.
105+
2. Click on "Developer settings" in the left sidebar.
106+
3. Click on "Personal access tokens" in the left sidebar.
107+
4. Click on "Tokens (classic)" or "Fine-grained tokens" depending on your preference.
108+
5. Click on "Generate new token".
109+
6. Give your token a name and select the scopes you want to grant. For this project, you don't need any specific scopes.
110+
7. Click on "Generate token".
111+
8. Copy the generated token.
112+
9. Set the `GITHUB_TOKEN` environment variable in your terminal or IDE:
113+
114+
```shell
115+
export GITHUB_TOKEN=your_personal_access_token
79116
```
80117

81-
2. For Azure OpenAI, create an Azure OpenAI gpt-3.5 or gpt-4 deployment (perhaps using [this template](https://github.com/Azure-Samples/azure-openai-keyless)), and customize the `.env` file with your Azure OpenAI endpoint and deployment id.
118+
10. Optionally, you can use a model other than "gpt-4o" by setting the `GITHUB_MODEL` environment variable. Use a model that supports function calling, such as: `gpt-4o`, `gpt-4o-mini`, `o3-mini`, `AI21-Jamba-1.5-Large`, `AI21-Jamba-1.5-Mini`, `Codestral-2501`, `Cohere-command-r`, `Ministral-3B`, `Mistral-Large-2411`, `Mistral-Nemo`, `Mistral-small`
82119

83-
```bash
84-
API_HOST=azure
85-
AZURE_OPENAI_ENDPOINT=https://YOUR-AZURE-OPENAI-SERVICE-NAME.openai.azure.com
86-
AZURE_OPENAI_DEPLOYMENT=YOUR-AZURE-DEPLOYMENT-NAME
87-
AZURE_OPENAI_VERSION=2024-03-01-preview
120+
## Using Azure OpenAI models
121+
122+
You can run all examples in this repository using GitHub Models. If you want to run the examples using models from Azure OpenAI instead, you need to provision the Azure AI resources, which will incur costs.
123+
124+
This project includes infrastructure as code (IaC) to provision Azure OpenAI deployments of "gpt-4o" and "text-embedding-3-large". The IaC is defined in the `infra` directory and uses the Azure Developer CLI to provision the resources.
125+
126+
1. Make sure the [Azure Developer CLI (azd)](https://aka.ms/install-azd) is installed.
127+
128+
2. Login to Azure:
129+
130+
```shell
131+
azd auth login
132+
```
133+
134+
For GitHub Codespaces users, if the previous command fails, try:
135+
136+
```shell
137+
azd auth login --use-device-code
138+
```
139+
140+
3. Provision the OpenAI account:
141+
142+
```shell
143+
azd provision
88144
```
89145

90-
If you are not yet logged into the Azure account associated with that deployment, run this command to log in:
146+
It will prompt you to provide an `azd` environment name (like "agents-demos"), select a subscription from your Azure account, and select a location. Then it will provision the resources in your account.
147+
148+
4. Once the resources are provisioned, you should now see a local `.env` file with all the environment variables needed to run the scripts.
149+
5. To delete the resources, run:
91150

92151
```shell
93-
az login
152+
azd down
94153
```
95154

96-
3. For OpenAI.com, customize the `.env` file with your OpenAI API key and desired model name.
155+
156+
## Using OpenAI.com models
157+
158+
1. Create a `.env` file by copying the `.env.sample` file and updating it with your OpenAI API key and desired model name.
97159

98160
```bash
99-
API_HOST=openai
100-
OPENAI_KEY=YOUR-OPENAI-API-KEY
101-
OPENAI_MODEL=gpt-3.5-turbo
161+
cp .env.sample .env
102162
```
103163

104-
4. For Ollama, customize the `.env` file with your Ollama endpoint and model name (any model you've pulled).
164+
2. Update the `.env` file with your OpenAI API key and desired model name:
105165

106166
```bash
107-
API_HOST=ollama
108-
OLLAMA_ENDPOINT=http://localhost:11434/v1
109-
OLLAMA_MODEL=llama2
167+
API_HOST=openai
168+
OPENAI_API_KEY=your_openai_api_key
169+
OPENAI_MODEL=gpt-4o-mini
110170
```
111171

112-
If you're running inside the Dev Container, replace `localhost` with `host.docker.internal`.
172+
## Using Ollama models
113173

114-
5. For GitHub models, customize the `.env` file with your GitHub model name.
174+
1. Install [Ollama](https://ollama.com/) and follow the instructions to set it up on your local machine.
175+
2. Pull a model, for example:
176+
177+
```shell
178+
ollama pull llama3.1
179+
```
180+
181+
3. Create a `.env` file by copying the `.env.sample` file and updating it with your Ollama endpoint and model name.
115182

116183
```bash
117-
API_HOST=github
118-
GITHUB_MODEL=gpt-4o
184+
cp .env.sample .env
119185
```
120186

121-
You'll need a `GITHUB_TOKEN` environment variable that stores a GitHub personal access token.
122-
If you're running this inside a GitHub Codespace, the token will be automatically available.
123-
If not, generate a new [personal access token](https://github.com/settings/tokens) and run this command to set the `GITHUB_TOKEN` environment variable:
187+
4. Update the `.env` file with your Ollama endpoint and model name (any model you've pulled):
124188
125-
```shell
126-
export GITHUB_TOKEN="<your-github-token-goes-here>"
189+
```bash
190+
API_HOST=ollama
191+
OLLAMA_ENDPOINT=http://localhost:11434/v1
192+
OLLAMA_MODEL=llama3.1
193+
```
127194
128195
## Resources
129196
197+
* [Upcoming October 2025 series: Python + AI](https://aka.ms/PythonAI/series)
130198
* [Video series: Learn Python + AI](https://techcommunity.microsoft.com/blog/EducatorDeveloperBlog/learn-python--ai-from-our-video-series/4400393)

azure.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
2+
3+
name: python-ai-agent-frameworks-demos
4+
metadata:
5+
6+
hooks:
7+
postprovision:
8+
windows:
9+
shell: pwsh
10+
run: ./infra/write_dot_env.ps1
11+
interactive: false
12+
continueOnError: false
13+
posix:
14+
shell: sh
15+
run: ./infra/write_dot_env.sh
16+
interactive: false
17+
continueOnError: false

chained_calls.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@
1212
token_provider = azure.identity.get_bearer_token_provider(
1313
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
1414
)
15-
client = openai.AzureOpenAI(
16-
api_version=os.environ["AZURE_OPENAI_VERSION"],
17-
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
18-
azure_ad_token_provider=token_provider,
15+
client = openai.OpenAI(
16+
base_url=os.environ["AZURE_OPENAI_ENDPOINT"],
17+
api_key=token_provider,
1918
)
20-
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]
19+
MODEL_NAME = os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT"]
2120

2221
elif API_HOST == "ollama":
2322
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")

chat.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@
1212
token_provider = azure.identity.get_bearer_token_provider(
1313
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
1414
)
15-
client = openai.AzureOpenAI(
16-
api_version=os.environ["AZURE_OPENAI_VERSION"],
17-
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
18-
azure_ad_token_provider=token_provider,
15+
client = openai.OpenAI(
16+
base_url=os.environ["AZURE_OPENAI_ENDPOINT"],
17+
api_key=token_provider,
1918
)
20-
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]
19+
MODEL_NAME = os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT"]
2120

2221
elif API_HOST == "ollama":
2322
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")

chat_async.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
import asyncio
22
import os
33

4-
import azure.identity
4+
import azure.identity.aio
55
import openai
66
from dotenv import load_dotenv
77

88
# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
99
load_dotenv(override=True)
1010
API_HOST = os.getenv("API_HOST", "github")
1111

12+
azure_credential = None # Will hold the Azure credential so we can close it properly.
1213
if API_HOST == "azure":
13-
token_provider = azure.identity.get_bearer_token_provider(
14-
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
14+
azure_credential = azure.identity.aio.DefaultAzureCredential()
15+
token_provider = azure.identity.aio.get_bearer_token_provider(
16+
azure_credential, "https://cognitiveservices.azure.com/.default"
1517
)
16-
client = openai.AsyncAzureOpenAI(
17-
api_version=os.environ["AZURE_OPENAI_VERSION"],
18-
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
19-
azure_ad_token_provider=token_provider,
18+
client = openai.AsyncOpenAI(
19+
base_url=os.environ["AZURE_OPENAI_ENDPOINT"],
20+
api_key=token_provider,
2021
)
21-
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]
22+
MODEL_NAME = os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT"]
2223
elif API_HOST == "ollama":
2324
client = openai.AsyncOpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
2425
MODEL_NAME = os.environ["OLLAMA_MODEL"]
@@ -52,11 +53,13 @@ async def generate_response(location):
5253
return response.choices[0].message.content
5354

5455

55-
async def single():
56+
async def single() -> None:
57+
"""Run a single request example and handle cleanup."""
5658
print(await generate_response("Tokyo"))
5759

5860

59-
async def multiple():
61+
async def multiple() -> None:
62+
"""Run multiple requests concurrently and handle cleanup."""
6063
answers = await asyncio.gather(
6164
generate_response("Tokyo"),
6265
generate_response("Berkeley"),
@@ -66,4 +69,19 @@ async def multiple():
6669
print(answer, "\n")
6770

6871

69-
asyncio.run(single())
72+
async def close_clients() -> None:
73+
"""Close the OpenAI async client and (if applicable) the Azure credential."""
74+
await client.close()
75+
if azure_credential is not None:
76+
await azure_credential.close()
77+
78+
79+
async def main():
80+
try:
81+
await single() # Change to await multiple() to run multiple requests concurrently
82+
finally:
83+
await close_clients()
84+
85+
86+
if __name__ == "__main__":
87+
asyncio.run(main())

chat_history.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@
1212
token_provider = azure.identity.get_bearer_token_provider(
1313
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
1414
)
15-
client = openai.AzureOpenAI(
16-
api_version=os.environ["AZURE_OPENAI_VERSION"],
17-
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
18-
azure_ad_token_provider=token_provider,
15+
client = openai.OpenAI(
16+
base_url=os.environ["AZURE_OPENAI_ENDPOINT"],
17+
api_key=token_provider,
1918
)
20-
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]
19+
MODEL_NAME = os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT"]
2120
elif API_HOST == "ollama":
2221
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
2322
MODEL_NAME = os.environ["OLLAMA_MODEL"]

chat_history_stream.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@
1212
token_provider = azure.identity.get_bearer_token_provider(
1313
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
1414
)
15-
client = openai.AzureOpenAI(
16-
api_version=os.environ["AZURE_OPENAI_VERSION"],
17-
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
18-
azure_ad_token_provider=token_provider,
15+
client = openai.OpenAI(
16+
base_url=os.environ["AZURE_OPENAI_ENDPOINT"],
17+
api_key=token_provider,
1918
)
20-
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]
19+
MODEL_NAME = os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT"]
2120
elif API_HOST == "ollama":
2221
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
2322
MODEL_NAME = os.environ["OLLAMA_MODEL"]

0 commit comments

Comments
 (0)