Skip to content

Commit 5038b48

Browse files
authored
Merge pull request #27 from pamelafox/structouts
Add demos for structured outputs
2 parents 0a4e74f + f1a8f21 commit 5038b48

13 files changed

+630
-1
lines changed

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ Then run the scripts (in order of increasing complexity):
4444
* [`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`.
4545
* [`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.
4646

47+
## Structured outputs with OpenAI
48+
49+
These scripts demonstrate how to use the OpenAI API to generate structured responses using Pydantic data models:
50+
51+
* [`structured_outputs_basic.py`](./structured_outputs_basic.py): Basic example extracting simple event information using a Pydantic model.
52+
* [`structured_outputs_description.py`](./structured_outputs_description.py): Uses additional descriptions in Pydantic model fields to clarify to the model how to format the response.
53+
* [`structured_outputs_enum.py`](./structured_outputs_enum.py): Uses enumerations (Enums) to restrict possible values in structured responses.
54+
* [`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.
55+
* [`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.
56+
4757
## Setting up the environment
4858

4959
If you open this up in a Dev Container or GitHub Codespaces, everything will be setup for you.

rag_ingested_chunks.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -243635,4 +243635,4 @@
243635243635
-0.012606247328221798
243636243636
]
243637243637
}
243638-
]
243638+
]

spanish/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ Luego ejecuta los scripts (en orden de complejidad creciente):
4343
* [`rag_documents_flow.py`](./rag_pdfs.py): Un flujo RAG que recupera resultados coincidentes del archivo JSON local creado por `rag_documents_ingestion.py`.
4444
* [`rag_documents_hybrid.py`](./rag_documents_hybrid.py): Un flujo RAG que implementa una recuperación híbrida con búsqueda vectorial y por palabras clave, fusionando con Reciprocal Rank Fusion (RRF), y reclasificación semántica con un modelo cross-encoder.
4545

46+
## Salidas estructuradas con OpenAI
47+
48+
Estos scripts muestran cómo usar la API de OpenAI para generar respuestas estructuradas usando modelos de datos con Pydantic:
49+
50+
* [`structured_outputs_basic.py`](./structured_outputs_basic.py): Ejemplo básico que extrae información sencilla de un evento usando un modelo Pydantic.
51+
* [`structured_outputs_description.py`](./structured_outputs_description.py): Usa descripciones adicionales en los campos del modelo Pydantic para aclararle al modelo cómo formatear la respuesta.
52+
* [`structured_outputs_enum.py`](./structured_outputs_enum.py): Usa enumeraciones (Enums) para restringir los valores posibles en la respuesta estructurada.
53+
* [`structured_outputs_function_calling.py`](./structured_outputs_function_calling.py): Muestra cómo usar funciones definidas con Pydantic para que el modelo las llame automáticamente según la consulta del usuario.
54+
* [`structured_outputs_nested.py`](./structured_outputs_nested.py): Usa modelos anidados con Pydantic para manejar respuestas estructuradas más complejas, como eventos con participantes que tienen múltiples atributos.
55+
56+
57+
4658
## Configuración del entorno
4759

4860
Si abres esto en un Dev Container o GitHub Codespaces, todo estará configurado para ti.

spanish/structured_outputs_basic.py

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import os
2+
3+
import azure.identity
4+
import openai
5+
import rich
6+
from dotenv import load_dotenv
7+
from pydantic import BaseModel
8+
9+
# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
10+
load_dotenv(override=True)
11+
API_HOST = os.getenv("API_HOST", "github")
12+
13+
if API_HOST == "azure":
14+
token_provider = azure.identity.get_bearer_token_provider(
15+
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
16+
)
17+
client = openai.AzureOpenAI(
18+
api_version=os.environ["AZURE_OPENAI_VERSION"],
19+
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
20+
azure_ad_token_provider=token_provider,
21+
)
22+
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]
23+
24+
elif API_HOST == "ollama":
25+
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
26+
MODEL_NAME = os.environ["OLLAMA_MODEL"]
27+
28+
elif API_HOST == "github":
29+
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
30+
MODEL_NAME = os.getenv("GITHUB_MODEL", "gpt-4o")
31+
32+
else:
33+
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
34+
MODEL_NAME = os.environ["OPENAI_MODEL"]
35+
36+
37+
class CalendarEvent(BaseModel):
38+
name: str
39+
date: str
40+
participants: list[str]
41+
42+
43+
completion = client.beta.chat.completions.parse(
44+
model=MODEL_NAME,
45+
messages=[
46+
{"role": "system", "content": "Extrae la info del evento."},
47+
{"role": "user", "content": "Alice y Bob van a ir a una feria de ciencias el viernes."},
48+
],
49+
response_format=CalendarEvent,
50+
)
51+
52+
53+
message = completion.choices[0].message
54+
if message.refusal:
55+
rich.print(message.refusal)
56+
else:
57+
event = message.parsed
58+
rich.print(event)
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import os
2+
3+
import azure.identity
4+
import openai
5+
import rich
6+
from dotenv import load_dotenv
7+
from pydantic import BaseModel, Field
8+
9+
# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
10+
load_dotenv(override=True)
11+
API_HOST = os.getenv("API_HOST", "github")
12+
13+
if API_HOST == "azure":
14+
token_provider = azure.identity.get_bearer_token_provider(
15+
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
16+
)
17+
client = openai.AzureOpenAI(
18+
api_version=os.environ["AZURE_OPENAI_VERSION"],
19+
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
20+
azure_ad_token_provider=token_provider,
21+
)
22+
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]
23+
24+
elif API_HOST == "ollama":
25+
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
26+
MODEL_NAME = os.environ["OLLAMA_MODEL"]
27+
28+
elif API_HOST == "github":
29+
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
30+
MODEL_NAME = os.getenv("GITHUB_MODEL", "gpt-4o")
31+
32+
else:
33+
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
34+
MODEL_NAME = os.environ["OPENAI_MODEL"]
35+
36+
37+
class CalendarEvent(BaseModel):
38+
name: str
39+
date: str = Field(..., description="A date in the format YYYY-MM-DD")
40+
participants: list[str]
41+
42+
43+
completion = client.beta.chat.completions.parse(
44+
model=MODEL_NAME,
45+
messages=[
46+
{
47+
"role": "system",
48+
"content": "Extrae la info del evento. Si no dice el año, asumí que es este año (2025).",
49+
},
50+
{"role": "user", "content": "Alice y Bob van a ir a una feria de ciencias el 1 de abril."},
51+
],
52+
response_format=CalendarEvent,
53+
)
54+
CalendarEvent(name="Feria de Ciencias", date="2025-04-01", participants=["Alice", "Bob"])
55+
message = completion.choices[0].message
56+
if message.refusal:
57+
rich.print(message.refusal)
58+
else:
59+
event = message.parsed
60+
rich.print(event)

spanish/structured_outputs_enum.py

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import os
2+
from enum import Enum
3+
4+
import azure.identity
5+
import openai
6+
import rich
7+
from dotenv import load_dotenv
8+
from pydantic import BaseModel
9+
10+
# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
11+
load_dotenv(override=True)
12+
API_HOST = os.getenv("API_HOST", "github")
13+
14+
if API_HOST == "azure":
15+
token_provider = azure.identity.get_bearer_token_provider(
16+
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
17+
)
18+
client = openai.AzureOpenAI(
19+
api_version=os.environ["AZURE_OPENAI_VERSION"],
20+
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
21+
azure_ad_token_provider=token_provider,
22+
)
23+
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]
24+
25+
elif API_HOST == "ollama":
26+
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
27+
MODEL_NAME = os.environ["OLLAMA_MODEL"]
28+
29+
elif API_HOST == "github":
30+
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
31+
MODEL_NAME = os.getenv("GITHUB_MODEL", "gpt-4o")
32+
33+
else:
34+
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
35+
MODEL_NAME = os.environ["OPENAI_MODEL"]
36+
37+
38+
class DayOfWeek(str, Enum):
39+
DOMINGO = "Domingo"
40+
LUNES = "Lunes"
41+
MARTES = "Martes"
42+
MIÉRCOLES = "Miércoles"
43+
JUEVES = "Jueves"
44+
VIERNES = "Viernes"
45+
SÁBADO = "Sábado"
46+
47+
48+
class CalendarEvent(BaseModel):
49+
name: str
50+
date: DayOfWeek
51+
participants: list[str]
52+
53+
54+
completion = client.beta.chat.completions.parse(
55+
model=MODEL_NAME,
56+
messages=[
57+
{"role": "system", "content": "Extrae la info del evento."},
58+
{"role": "user", "content": "Alice y Bob van a ir a una feria de ciencias el viernes."},
59+
],
60+
response_format=CalendarEvent,
61+
)
62+
63+
64+
message = completion.choices[0].message
65+
if message.refusal:
66+
rich.print(message.refusal)
67+
else:
68+
event = message.parsed
69+
rich.print(event)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import os
2+
3+
import azure.identity
4+
import openai
5+
import rich
6+
from dotenv import load_dotenv
7+
from pydantic import BaseModel
8+
9+
# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
10+
load_dotenv(override=True)
11+
API_HOST = os.getenv("API_HOST", "github")
12+
13+
if API_HOST == "azure":
14+
token_provider = azure.identity.get_bearer_token_provider(
15+
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
16+
)
17+
client = openai.AzureOpenAI(
18+
api_version=os.environ["AZURE_OPENAI_VERSION"],
19+
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
20+
azure_ad_token_provider=token_provider,
21+
)
22+
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]
23+
24+
elif API_HOST == "ollama":
25+
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
26+
MODEL_NAME = os.environ["OLLAMA_MODEL"]
27+
28+
elif API_HOST == "github":
29+
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
30+
MODEL_NAME = os.getenv("GITHUB_MODEL", "gpt-4o")
31+
32+
else:
33+
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
34+
MODEL_NAME = os.environ["OPENAI_MODEL"]
35+
36+
37+
class GetDeliveryDate(BaseModel):
38+
order_id: str
39+
40+
41+
response = client.chat.completions.create(
42+
model=MODEL_NAME,
43+
messages=[
44+
{
45+
"role": "system",
46+
"content": "Eres un bot de atención al cliente. Usá las herramientas para ayudar al usuario.",
47+
},
48+
{"role": "user", "content": "Hola, ¿me puedes decir cuándo llegará mi pedido #12345?"},
49+
],
50+
tools=[openai.pydantic_function_tool(GetDeliveryDate)],
51+
)
52+
53+
rich.print(response.choices[0].message.tool_calls[0].function)

spanish/structured_outputs_nested.py

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import os
2+
3+
import azure.identity
4+
import openai
5+
import rich
6+
from dotenv import load_dotenv
7+
from pydantic import BaseModel
8+
9+
# Setup the OpenAI client to use either Azure, OpenAI.com, or Ollama API
10+
load_dotenv(override=True)
11+
API_HOST = os.getenv("API_HOST", "github")
12+
13+
if API_HOST == "azure":
14+
token_provider = azure.identity.get_bearer_token_provider(
15+
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
16+
)
17+
client = openai.AzureOpenAI(
18+
api_version=os.environ["AZURE_OPENAI_VERSION"],
19+
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
20+
azure_ad_token_provider=token_provider,
21+
)
22+
MODEL_NAME = os.environ["AZURE_OPENAI_DEPLOYMENT"]
23+
24+
elif API_HOST == "ollama":
25+
client = openai.OpenAI(base_url=os.environ["OLLAMA_ENDPOINT"], api_key="nokeyneeded")
26+
MODEL_NAME = os.environ["OLLAMA_MODEL"]
27+
28+
elif API_HOST == "github":
29+
client = openai.OpenAI(base_url="https://models.inference.ai.azure.com", api_key=os.environ["GITHUB_TOKEN"])
30+
MODEL_NAME = os.getenv("GITHUB_MODEL", "gpt-4o")
31+
32+
else:
33+
client = openai.OpenAI(api_key=os.environ["OPENAI_KEY"])
34+
MODEL_NAME = os.environ["OPENAI_MODEL"]
35+
36+
37+
class Participant(BaseModel):
38+
name: str
39+
job_title: str
40+
41+
42+
class CalendarEvent(BaseModel):
43+
name: str
44+
date: str
45+
participants: list[Participant]
46+
47+
48+
completion = client.beta.chat.completions.parse(
49+
model=MODEL_NAME,
50+
messages=[
51+
{"role": "system", "content": "Extrae la info del evento."},
52+
{
53+
"role": "user",
54+
"content": "Alice, que es carpintera, y Bob, que es plomero, van a ir a una feria de ciencias el viernes.",
55+
},
56+
],
57+
response_format=CalendarEvent,
58+
)
59+
60+
61+
message = completion.choices[0].message
62+
if message.refusal:
63+
rich.print(message.refusal)
64+
else:
65+
event = message.parsed
66+
rich.print(event)

0 commit comments

Comments
 (0)