global
for sessions, yield
for streaming, and print
for anything you wanna display in your app. Install Funix (advanced installation options):
pip install funix
Save the code below to a file called hello.py
:
def hello(your_name: str) -> str:
return f"Hello, {your_name}."
Running this command
$ funix hello.py
The app will pop in a browser on http://localhost:3000
:
The Zen of Funix is to generate widgets for function I/Os based on their types, instead of manually picking and customizing the widget for each I/O. Funix to Python is like CSS to HTML or style class to LaTeX. UI stuff is not intermingled with the core logic but defined separately. The type-to-widget mapping is controlled by a theme and new types can be defined on top of existing types.
The example below shows how common UI compoents are generated from four Python-native data types: str
, bool
, Literal
and range
, as well as types in popular packages such as ipywidgets.Password
. The example below further maps default values to placeholders in the UI widgets. From this example, we can see that developers need to learn nothing about Funix or do nothing to their existing code before they can get an app.
import typing # Python native
import ipywidgets # popular UI library
def input_widgets_basic(
prompt: str = "Who is Oppenheimer?",
advanced_features: bool = True,
model: typing.Literal['GPT-3.5', 'GPT-4.0', 'Falcon-7B'] = 'GPT-4.0',
max_token: range(100, 200, 20) = 140,
openai_key: ipywidgets.Password = "1234556",
) -> str:
pass
The example below shows how pandas.DataFrame
and matplotlib.figure.Figure
that AI/data developers cannot be more familiar with are mapped to tables and charts in an app.
import pandas, matplotlib.pyplot
from numpy import arange, log
from numpy.random import random
def table_and_plot(
df: pandas.DataFrame = pandas.DataFrame({
"a": arange(500) + random(500)/5,
"b": random(500)-0.5 + log(arange(500)+1),
"c": log(arange(500)+1) })
) -> matplotlib.figure.Figure:
fig = matplotlib.pyplot.figure()
matplotlib.pyplot.plot(df["a"], df["b"], 'b')
matplotlib.pyplot.plot(df["a"], df["c"], 'r')
return fig
You can even continuously update a plot based on user input.
import funix
import matplotlib.pyplot, matplotlib.figure
import numpy
@funix.funix(
autorun=True,
)
def sine(omega: funix.hint.FloatSlider(0, 4, 0.1)) -> matplotlib.figure.Figure:
fig = matplotlib.pyplot.figure()
x = numpy.linspace(0, 20, 200)
y = numpy.sin(x*omega)
matplotlib.pyplot.plot(x, y, linewidth=5)
return fig
Funix can turn each member method of a class into a page of an app. In two additional lines, the example below turn a class definition into a multi-page app with the OOP experience where the instantiation of an object of the class is done in a page corresponding to the constructor of the class and the member values of the object can be viewed and updated in other pages. No need to manually expose the member methods of the class.
from funix import funix_class
@funix_class()
class A:
def __init__(self, a: int):
self.a = a
return f"`self.a` has been initialized to {self.a}"
def set(self, b: int) -> str:
self.a = b
return f"`self.a` has been updated to {self.a}"
def get(self) -> str:
return f"The value of `self.a` is {self.a}"
A more advanced example of class is the wordle game. You can find the source code in examples/wordle.py
. In less than 100 lines, you can build Wordle! The screenshot is as follows:
You can wrap any Python function into a web app in Funix. For example, you can take the ChatGPT demo code from OpenAI's APi reference and wrap it into a str-to-str function:
from openai import OpenAI
def ChatGPT(prompt: str) -> str:
client = OpenAI()
completion = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "user", "content": prompt}
]
)
return completion.choices[0].message.content
which is turned into an app by Funix like below:
Funix to Python is like CSS to HTML or macros to LaTeX. It separates the core logic and the UI. All UI stuff is centrally defined in a JSON-based theme to avoid the repetitiveness of individually configuring widgets and keep a consistent look across apps. Consequently, a data scientist or a machine learning engineer does not need to think about anything UI. Just leave it to the UI team and Funix.
Below is an example of a theme file. It defines the widget choices based on variable types and tweaks the props
of UI components (currently only MUI ones are supported). Funix exposes frontend components and their props
to developers, requiring them to know nothing about frontend. To know more about how to define and apply a theme, please refer to the Themes section in the reference manual.
{
"name": "test_theme",
"widgets": { // dict, map types to widgets
"str": "inputbox", // using Funix' widget shorthand
"int": "slider[0,100,2]", // using Funix' widget shorthand
"float": {
"widget": "@mui/material/Slider", // using MUI's widget
// https://mui.com/material-ui/api/slider
"props": {
"min": 0,
"max": 100,
"step": 0.1
}
},
"Literal": "radio"
},
}
To introduce a new data type, just declare a new Python class, and use a decorator to let Funix know. You can associate the type with a widget either on-the-fly (below) or via a theme file.
from funix import funix, new_funix_type
@new_funix_type(
widget = {
"widget": "@mui/material/TextField",
"props": {
"type": "password",
"placeholder": "Enter a secret here."
}
}
)
class blackout(str):
def print(self):
return self + " is the message."
@funix()
def hoho(x: blackout = "Funix Rocks!") -> str:
return x.print()
if __name__ == "__main__":
print (hoho(blackout('Fun')))
Funix repurposes some Python-native features to web features.
First, Funix won't let your docstring or print()
function calls go to waste. They will appear in the input and output panels of the web app.
from funix import funix
@funix(
print_to_web=True
)
def foo() -> None:
"""
## What a great app in Funix!
Funix won't let your docstring go to waste.
"""
print("It supports **Markdown**.")
print ("And <b>HTML</b>.")
return None
To stream text to the web, simply use yield
:
import time
def stream() -> str:
"""
This function is used to test the stream feature of Funix.
"""
message = "We the People of the United States, in Order to form a more perfect Union, establish Justice, insure domestic Tranquility, provide for the common defence, promote the general Welfare, and secure the Blessings of Liberty to ourselves and our Posterity, do ordain and establish this Constitution for the United States of America."
for i in range(len(message)):
time.sleep(0.01)
yield message[0:i]
More examples in Reference Manual or the ./examples
folder.
examples/AI/chatGPT_multi_turn.py
👈 Toggle me to show source code from funix import funix # add line one
from IPython.display import Image
from openai import OpenAI # pip install openai
import os
client = OpenAI(api_key=os.environ.get("OPENAI_KEY"))
@funix() # add line two
def dalle(prompt: str = "a cat") -> Image:
response = client.images.generate(prompt=prompt)
return response.data[0].url
examples/bioinformatics/vector_strip.py
👈 Click to see source code
examples/multimedia/rgb2gray.py
👈 Toggle me to show source codeexamples/layout_easypost_shipping.py
👈 Click me to see source code
pip install funix
From GitHub (latest)
pip install "git+https://github.com/TexteaInc/funix.git@develop" # Develop version
pip install "git+https://github.com/TexteaInc/funix.git" # Stable version
Local development
git clone -b develop https://github.com/TexteaInc/funix
cd funix
pip install -e .
Add --prefix=~/.local
if pip insists to install to system paths. See #24 and #23
If you want to use git
related features (use project from GitHub), install funix by:
pip install funix[git]
If you want to use ipython
features, install funix by:
pip install funix[ipython]
If you need to have more than one feature, you can try installing them together:
pip install funix[all]
In the last two cases above, you will need to compile the frontend by yourself. Suppose you are in the funix
folder. Then run the following commands:
cd frontend
yarn install
(you need install Node.JS and Yarn)yarn funix:build
For debugging:
yarn funix:test
(Start development mode)funix xxx -p 8080 -F
(Use 8080 port and no frontend mode)Our table widget uses advanced features in MUI Pro. If you have a MUI Pro license, you can build the frontend with MUI Pro by following the steps below:
export MUI_PRO_LICENSE_KEY=[MUI_KEY]
;yarn install && yarn funix:build
to build the frontend;Run the command below to see the command line options:
funix -h
funix
in PythonBesides starting Funix from the command line, you can also use a self-contained .py
script:
import funix
@funix.funix()
def hello(your_name: str) -> str:
return f"Hello, {your_name}."
if __name__ == "__main__":
funix.run(__file__)
funix [module] --host [your_server_ip]
Funix is open-sourced under the MIT License. Community contribution is not only welcomed but desired. Feel free to fork and make a pull request when you are ready. You can also report bugs, suggest new features via the issue tracker or our Discord server.
Funix draws inspiration from FastAPI and Python-Fire: building software interfaces by inferring from function signatures containing type hints. We port this idea from the backend (FastAPI) or the terminal (Python-Fire) to the frontend. We also thank Streamlit, Gradio, PyWebIO, and Pynecone/Reflex. They inspired us. We are just too lazy to manually define widgets imperatively. Funix’s backend is implemented in Flask and the frontend in Material UI.
Forrest dot Bao @ Gmail