Example Python Application
Provides and explains how to build your own chat application utilizing our (your own deployment of our) API within a flask application.
Last updated
Provides and explains how to build your own chat application utilizing our (your own deployment of our) API within a flask application.
Last updated
Poetry used for package management
Non-src-based layout
I.e., the main package is adjacent to the pyproject.toml
file (since the source code is important, when usually one would use poetry to build the package and send a wheel of it)
Why Poetry?
Automatically and cleanly handles cross-package dependencies, installing valid versions for cross-compatibility if possible
Virtual Environment Management
Swagger Page: https://demo.aicrisk.com/Swagger/index.html
NOTE: Outside of the demo server, replace https://demo.aicrisk.com
with your deployed endpoint
Setup: Rename example.env
to .env
(Or if there is already a .env file
, edit that) and fill in CLIENT_ID
and CLIENT_SECRET
and any other configuration
For Example, set USER_ID
if wanting the app to automatically select you (and even sign you in automatically if AUTO_LOG_ME_IN
is True) as the user to authenticate
Ensure proper url to deployment in API_BASE_URL
cd into directory containing pyproject.toml
poetry install
CD into airisk_python_demo/airisk_python_demo
Run Dev: poetry run flask run --port 7221 --cert=adhoc
Run with debugger: poetry run flask run --port 7221 --cert=adhoc --debugger --debug
When Signed in as User, Impersonate User
button allows impersonation of another user without losing initial user token
Exit Impersonation
button when impersonating someone will remove active impersonation and will sign in as initial user.
Data Endpoints
Section
View the literal JSON response data from corresponding remote endpoints
UI Endpoints
Examples of using remote data in ones own UI
Select Navigator
section
View remote data on things relative to other things
Ex. Given current authenticated users available agents, go to conversation data for one agent (the one selected in the dropdown)
Endpoint with Form Link
Example of integrating ones own form and flask view to handle the data input, and to communicate with the remote API with some sore of creation action. Ex. Creating a conversation.
Forms
(Deprecated, replaced with "Option Name" section)Ask Form Page
Rough example of chat-less interface for using the remote /api/Chat/Ask endpoint
Single Object Navigator Util
List Conversations JSON
Button
JSON data view for all authenticated conversations
Option Name ...
section
Navigate to a custom chat page with which communicates with the remote server, providing a ChatGPT-like interface for an entire conversation.
Note, since this example app isn't utilizing streaming, one could either adapt this app to do so, or could create an illusion of streaming.
Since in demo.aicrisk.com
, the callback URLs allowed are HTTPS and not HTTP (e.g. https://localhost:7221/callback
), therefore it is necessary to make flask run with https
needed pyopenssl
and to append --cert=adhoc
to flask cli (or equiv to app.run
)
Like #1, the callback URLs specify a port 7221
.
Need to run flask with --port 7221
or change the setting
Using Jinja2 as a template language for rendering the front-end HTML based UI using data coming from python
.env
Contains config variables for a more dynamic approach
const.py
loads from .env
into env variables also defining defaults.
Also defines TokenInput
class for persistence of tokens with support for auto-saving, auto-loading, expiration-aware auth, etc.
decorators.py
@login_required
Custom decorator to redirect client to /
url (where auth is invoked) for any views accessing the remote api and therefore needing authentication.
If running server in debug mode, making a change & saving and then refreshing the page on a non-root URL, the page will fail since it would be obnoxious to implement authentication logic in every view and since these views require a token.
authentication/
Module containing the flask Blueprint encapsulating the app logic (to show how to separate concerns.)
Could have set custom url prefix for this as well but didn't since didn't want to change aicrisk callback urls
contains view functions and data retrieval utilities along with some state management therein
templates/
Directory containing the jinja2 templates used across the project
base.html
is the root template (all others 'extend' it)
i.e. when extending a template, you inherit everything by default, including blocks. If defining an extisting block in the child, the whole block from the parent is overridden (unless doing super call therein)
Added CDN for tailwind css for easier style prototyping
Make sure to set variables in .env properly. Noteably the CLIENT_ID
and CLIENT_SECRET
. Also the REDIRECT_URI if on a non-demo instance of AICRisk
Ensure callback urls are set in the remote server admin
If running locally, https://localhost:{PORT}
(+/callback
, ...)
Otherwise, set the exact IP or domain in which the local server will be run on
If wanting to automatically sign in as oneself (assuming USER_ID
is set), also set AUTO_LOG_ME_IN=True
On start, the variables in .env
are loaded into environment variables with fallbacks in const.py
flask runs app.py (using configured variables) which in turn uses the app factory pattern to register the auth_bp blueprint and its routes to the app
User opens browser to page
If the page is the auth_index page
If AUTO_RESTORE_SESSION
is True and if the file at the path of TOKEN_INFO_FILE
exists and contains a valid, not-expired token, then that token is read, validated, and set within the session as "access_token"
Otherwise
If AUTO_RESTORE_SESSION
is False but the token file exists and contains a valid, non-expired token
The page is rendered with links to
Go through the auth process from scratch, invoked by redirecting to auth
view
Read & load token from the file (see TokenInfo.load
)
Otherwise (token non-existent or expired)
The page is rendered with a link to go through the authentication process
Otherwise (any other page that also isn't the authentication view (auth
))
The client is redirected to auth_index
page and is provided same means of authenticating as above accordingly
Once auth
is invoked
Starts by passing client_id
(from env) and redirect_uri (https://{HOST|localhost}:{PORT|7221}/callback
) to the remote authorize endpoint via a GET request (/api/oauth2
) with encoded params placed in the url directly
The remote server then does its thing internally and redirects the client browser to the passed redirect_url
(url for oauth_callback
view) with a code
parameter passed along therefrom
The redirect urls corresponding view function (oauth_callback
) then issues a POST request to the remote token endpoint (/api/oauth2/Token
) with parameters for the client_id
, client_secret
, grant_type="authorization_code"
, redirect_uri
, and code
with the value of code being that value received from the remote authorize endpoint
If successful and yielding a valid response from that POST request, a variable is then pulled from the returned json for access_token
and is set in the state and saved. This is the JWT/Bearer Token we needed for the rest of the apps views to work
Custom UI components are rendered to simplify navigation and to illustrate how to somewhat dynamically set up linking to the other pages
For the list views, a given remote endpoint (ex user list) has
a corresponding local url for retrieving the data.
(Optionally) Some also have a corresponding "*_ui" view which renders the data in a nice user-centric fashion
Also illustrate how one may set up form field choices based on the existing remote data fetched. Ex. selecting conversations only for a particular agent
possible states when in app (non-linear)
Not Authenticated - [client_id, client_secret]
Authenticated as Generic
has token
Authenticated as Generic then User
User Authenticated directly without generic
User Authenticated somehow, and impersonating another user