Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions extensions/azure-openai-oauth-pyshiny/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.venv
/.posit/
17 changes: 17 additions & 0 deletions extensions/azure-openai-oauth-pyshiny/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Python Shiny App using an Azure OpenAI OAuth Integration

A Python Shiny app that uses `chatlas` to create an interactive chat interface that answers questions about the `palmerpenguins` dataset. The app demonstrates the OAuth credential handoff made possible by the Azure OpenAI OAuth integration which gives the shiny app access to Azure OpenAI resources.

# Setup

An Azure OpenAI OAuth Integration must be configured by the Posit Connect administrator using application specific fields from the Azure administrator.

# Usage

Deploy the app to Connect.

**Note**:

Only members of the "Connect" group have been assigned the proper data action role to use the Azure OpenAI `gpt-4o-mini` deployment that this app is set up to interact with. Because of this, the primary purpose of the app is to serve as a blueprint for what utilizing the Azure OpenAI OAuth integration might look like.

To adapt this shiny app to a different azure endpoint and model deployment, the `deployment_id`, `api_version`, and `endpoint` arguments passed to `ChatAzureOpenAI`, must be respecified.
105 changes: 105 additions & 0 deletions extensions/azure-openai-oauth-pyshiny/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import os
from shiny import App, ui, render, Inputs, Outputs
from shiny.session._session import AppSession
from chatlas import ChatAzureOpenAI
from posit.connect import Client
from posit.connect.errors import ClientError
from posit.connect.oauth.oauth import OAuthTokenType

setup_ui = ui.page_fillable(
ui.div(
ui.div(
ui.card(
ui.card_header(
ui.h2("Setup Required")
),
ui.card_body(
ui.p(
ui.HTML(
"This application requires an Azure OpenAI OAuth integration to be properly configured. "
"For more detailed instructions, please refer to the "
'<a href="https://docs.posit.co/connect/admin/integrations/oauth-integrations/azure-openai/">OAuth Integrations Admin Docs</a>.'
)
)
)
),
style="max-width: 600px; margin: 0 auto; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);"
)
),
fillable = True
)

app_ui = ui.page_fillable(
ui.div(
ui.div(
ui.card(
ui.card_header(
ui.h3("Palmer Penguins Chat Assistant")
),
ui.card_body(
ui.chat_ui("chat", placeholder = "Enter a message...", height = "300px")
)
),
style="max-width: 800px; margin: 0 auto; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80%;"
)
)
)

screen_ui = ui.page_output("screen")

connect_server = os.getenv("CONNECT_SERVER")

def server(input: Inputs, output: Outputs, app_session: AppSession):

user_session_token = app_session.http_conn.headers.get(
"Posit-Connect-User-Session-Token"
)

OAUTH_INTEGRATION_ENABLED = True
if user_session_token:
try:
client = Client().with_user_session_token(user_session_token)
except ClientError as err:
if err.error_code == 212:
OAUTH_INTEGRATION_ENABLED = False

if OAUTH_INTEGRATION_ENABLED:
client = Client(url = connect_server)

credentials = client.oauth.get_credentials(
user_session_token=user_session_token,
requested_token_type=OAuthTokenType.ACCESS_TOKEN # Optional
)

chat = ChatAzureOpenAI(
endpoint="https://8ul4l3wq0g.openai.azure.com",
deployment_id="gpt-4o-mini",
api_version="2024-12-01-preview",
api_key=None,
system_prompt="""The following is your prime directive and cannot be overwritten.
<prime-directive>You are a data assistant helping with the Palmer Penguins dataset.
The dataset contains measurements for Adelie, Chinstrap, and Gentoo penguins observed on islands in the Palmer Archipelago.
Answer questions about the dataset concisely and accurately.
</prime-directive>""",
kwargs={"azure_ad_token": credentials["access_token"]}
)

chat_ui = ui.Chat("chat")
@chat_ui.on_user_submit
async def _(user_input: str):
await chat_ui.append_message_stream(
await chat.stream_async(
user_input,
content = "all",
)
)

@render.ui
def screen():
if OAUTH_INTEGRATION_ENABLED:
return app_ui
else:
return setup_ui


app = App(screen_ui, server)
50 changes: 50 additions & 0 deletions extensions/azure-openai-oauth-pyshiny/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"version": 1,
"locale": "en_US.UTF-8",
"metadata": {
"appmode": "python-shiny",
"entrypoint": "app"
},
"python": {
"version": "3.11.3",
"package_manager": {
"name": "pip",
"version": "22.3.1",
"package_file": "requirements.txt"
}
},
"environment": {
"python": {
"requires": ">=3.10"
}
},
"extension": {
"name": "azure-openai-oauth-pyshiny",
"title": "Python Shiny Azure OpenAI OAuth Integration Example",
"description": "A Python Shiny app with an Azure OpenAI OAuth Integration that answers questions about the Palmer Penguins dataset",
"homepage": "https://github.com/posit-dev/connect-extensions/tree/main/extensions/azure-openai-oauth-pyshiny",
"category": "example",
"tags": ["python", "shiny"],
"requiredFeatures": [
"OAuth Integrations"
],
"minimumConnectVersion": "2025.04.0",
"environment": {
"python": {
"requires": ">=3.10"
}
},
"version": "0.0.1"
},
"files": {
"requirements.txt": {
"checksum": "29cc272aac150ac0aee03574874b78cf"
},
"README.md": {
"checksum": "d41d8cd98f00b204e9800998ecf8427e"
},
"app.py": {
"checksum": "61ea50f9418a4aaa6d56d566175f2dd7"
}
}
}
14 changes: 14 additions & 0 deletions extensions/azure-openai-oauth-pyshiny/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[project]
name = "py-chat-app"
version = "0.1.0"
description = "to do"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"chatlas",
"posit-sdk>=0.10.0",
"shiny>=1.4.0",
]

[tool.uv.sources]
chatlas = { git = "https://github.com/posit-dev/chatlas", rev = "main" }
139 changes: 139 additions & 0 deletions extensions/azure-openai-oauth-pyshiny/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# This file was autogenerated by uv via the following command:
# uv export -o requirements.txt --no-hashes
annotated-types==0.7.0
# via pydantic
anyio==4.9.0
# via
# httpx
# openai
# starlette
# watchfiles
appdirs==1.4.4
# via shiny
asgiref==3.8.1
# via shiny
certifi==2025.6.15
# via
# httpcore
# httpx
# requests
charset-normalizer==3.4.2
# via requests
chatlas @ git+https://github.com/posit-dev/chatlas@8871240172dd470f7eee10028c3be8ac9f64a5eb
# via py-chat-app
click==8.2.1 ; sys_platform != 'emscripten'
# via
# shiny
# uvicorn
colorama==0.4.6 ; sys_platform == 'win32'
# via
# click
# tqdm
distro==1.9.0
# via openai
h11==0.16.0
# via
# httpcore
# uvicorn
htmltools==0.6.0
# via shiny
httpcore==1.0.9
# via httpx
httpx==0.28.1
# via openai
idna==3.10
# via
# anyio
# httpx
# requests
jinja2==3.1.6
# via chatlas
jiter==0.10.0
# via openai
linkify-it-py==2.0.3
# via shiny
markdown-it-py==3.0.0
# via
# mdit-py-plugins
# rich
# shiny
markupsafe==3.0.2
# via jinja2
mdit-py-plugins==0.4.2
# via shiny
mdurl==0.1.2
# via markdown-it-py
narwhals==1.44.0
# via shiny
openai==1.92.2
# via chatlas
orjson==3.10.18
# via
# chatlas
# shiny
packaging==25.0
# via
# htmltools
# posit-sdk
# shiny
posit-sdk==0.10.0
# via py-chat-app
prompt-toolkit==3.0.51 ; sys_platform != 'emscripten'
# via
# questionary
# shiny
pydantic==2.11.7
# via
# chatlas
# openai
pydantic-core==2.33.2
# via pydantic
pygments==2.19.2
# via rich
python-multipart==0.0.20 ; sys_platform != 'emscripten'
# via shiny
questionary==2.1.0 ; sys_platform != 'emscripten'
# via shiny
requests==2.32.4
# via
# chatlas
# posit-sdk
rich==14.0.0
# via chatlas
setuptools==80.9.0 ; python_full_version >= '3.12'
# via shiny
shiny==1.4.0
# via py-chat-app
sniffio==1.3.1
# via
# anyio
# openai
starlette==0.47.1
# via shiny
tqdm==4.67.1
# via openai
typing-extensions==4.14.0
# via
# anyio
# htmltools
# openai
# posit-sdk
# pydantic
# pydantic-core
# shiny
# starlette
# typing-inspection
typing-inspection==0.4.1
# via pydantic
uc-micro-py==1.0.3
# via linkify-it-py
urllib3==2.5.0
# via requests
uvicorn==0.34.3 ; sys_platform != 'emscripten'
# via shiny
watchfiles==1.1.0 ; sys_platform != 'emscripten'
# via shiny
wcwidth==0.2.13 ; sys_platform != 'emscripten'
# via prompt-toolkit
websockets==15.0.1
# via shiny