Skip to content

Commit 5bca05b

Browse files
A simple Ollama web interface made with Flask.
1 parent 6bff249 commit 5bca05b

File tree

7 files changed

+383
-0
lines changed

7 files changed

+383
-0
lines changed

en.ini

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[LangContent]
2+
your_password = Your password
3+
password_recovery_failed = Password recovery failed! The password you entered does not sufficiently match the registered password.
4+
app_title = A simple Ollama web interface made with Flask
5+
user_name_input = Username
6+
password_input = Password
7+
login_var = Log in
8+
user_ = User
9+
new_user_register = New user registration
10+
login_successfully = Logged in!
11+
welcome = Welcome
12+
go_to_app = Go to app
13+
login_failed = Login failed!
14+
login_failed_description = Username or password is incorrect.
15+
user_register = Register user
16+
try_again = Try again
17+
password_forget = I forgot my password
18+
password_recovery = Password recovery
19+
password_recovery_ = Password recovery for user account
20+
password_recovery_input = Please enter a similar password that you remember for your
21+
pass_rec_ = account...
22+
password_recovery_submit = Recover password
23+
password_recovery_report = Password recovery result
24+
user_register_failed = This username is already taken!
25+
user_register_ip = This IP address is already registered!
26+
register = Register
27+
user_register_failed_title = Registration failed!
28+
user_register_failed_description = Password cannot be blank.
29+
user_register_successfully = Registration successful!
30+
user_register_successfully_description = You can now log in.
31+
ollama_web_ui_title = You can use the AI models installed in your Ollama application here.
32+
user_model = Enter the model you want to use. Example: llama3:latest
33+
model_question = Please ask your model a question.
34+
model_submit = Ask a question
35+
exit = Log out
36+
app_requirements = You need to log in!
37+
app_requirements_description = You must first log in to use LXLM-Ollama-WebUI.

lang.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[WebUILang]
2+
lang = EN
3+

lang.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import configparser
2+
3+
lang = configparser.ConfigParser()
4+
lang.read('lang.ini')
5+
6+
def set_lang(userlang):
7+
lang["WebUILang"]["lang"] = userlang
8+
with open('lang.ini', 'w') as f:
9+
lang.write(f)
10+
11+
user_lang = lang["WebUILang"]["lang"]
12+
13+
lang_file = configparser.ConfigParser()
14+
lang_file.read(f"{user_lang}.ini", encoding="utf-8")
15+
16+
your_password = lang_file["LangContent"]["your_password"]
17+
password_recovery_failed = lang_file["LangContent"]["password_recovery_failed"]
18+
app_title = lang_file["LangContent"]["app_title"]
19+
user_name_input = lang_file["LangContent"]["user_name_input"]
20+
password_input = lang_file["LangContent"]["password_input"]
21+
login_var = lang_file["LangContent"]["login_var"]
22+
new_user_register = lang_file["LangContent"]["new_user_register"]
23+
login_successfully = lang_file["LangContent"]["login_successfully"]
24+
welcome = lang_file["LangContent"]["welcome"]
25+
go_to_app = lang_file["LangContent"]["go_to_app"]
26+
login_failed = lang_file["LangContent"]["login_failed"]
27+
user_ = lang_file["LangContent"]["user_"]
28+
login_failed_description = lang_file["LangContent"]["login_failed_description"]
29+
user_register = lang_file["LangContent"]["user_register"]
30+
try_again = lang_file["LangContent"]["try_again"]
31+
pass_rec_ = lang_file["LangContent"]["pass_rec_"]
32+
user_register_ip = lang_file["LangContent"]["user_register_ip"]
33+
password_forget = lang_file["LangContent"]["password_forget"]
34+
password_recovery = lang_file["LangContent"]["password_recovery"]
35+
password_recovery_ = lang_file["LangContent"]["password_recovery_"]
36+
password_recovery_input = lang_file["LangContent"]["password_recovery_input"]
37+
password_recovery_submit = lang_file["LangContent"]["password_recovery_submit"]
38+
password_recovery_report = lang_file["LangContent"]["password_recovery_report"]
39+
user_register_failed = lang_file["LangContent"]["user_register_failed"]
40+
register = lang_file["LangContent"]["register"]
41+
user_register_failed_title = lang_file["LangContent"]["user_register_failed_title"]
42+
user_register_failed_description = lang_file["LangContent"]["user_register_failed_description"]
43+
user_register_successfully = lang_file["LangContent"]["user_register_successfully"]
44+
user_register_successfully_description = lang_file["LangContent"]["user_register_successfully_description"]
45+
ollama_web_ui_title = lang_file["LangContent"]["ollama_web_ui_title"]
46+
user_model = lang_file["LangContent"]["user_model"]
47+
model_question = lang_file["LangContent"]["model_question"]
48+
model_submit = lang_file["LangContent"]["model_submit"]
49+
exit = lang_file["LangContent"]["exit"]
50+
app_requirements = lang_file["LangContent"]["app_requirements"]
51+
app_requirements_description = lang_file["LangContent"]["app_requirements_description"]

ollama_model.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#from ollama import ChatResponse, chat
2+
import subprocess
3+
def load_model(model_name, msg):
4+
"""messages = [
5+
{
6+
"role": "user",
7+
"content": msg
8+
},
9+
]
10+
response: ChatResponse = chat(model=model_name, messages=messages)
11+
return response.message.content"""
12+
model_response = subprocess.run(["ollama", "run", model_name, msg], capture_output=True, text=True, encoding="utf-8")
13+
model_response = model_response.stdout.strip()
14+
return model_response

tr.ini

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[LangContent]
2+
your_password = Şifreniz
3+
password_recovery_failed = Şifre kurtarma başarısız! Girdiğiniz şifre, kayıtlı şifreye yeterince benzemiyor.
4+
app_title = Flask ile yapılmış basit bir Ollama web arayüzü
5+
user_name_input = Kullanıcı Adı
6+
password_input = Şifre
7+
login_var = Giriş Yap
8+
user_ = Kullanıcı
9+
new_user_register = Yeni kullanıcı kaydı
10+
login_successfully = Giriş Yapıldı!
11+
welcome = Hoş geldiniz
12+
go_to_app = Uygulamaya git
13+
login_failed = Giriş başarısız!
14+
login_failed_description = Kullanıcı adı veya şifre yanlış.
15+
user_register = Kullanıcı kaydet
16+
try_again = Tekrar dene
17+
password_forget = Şifremi unuttum
18+
password_recovery = Şifre kurtarma
19+
password_recovery_ = Kullanıcı hesabı için şifre kurtarma
20+
password_recovery_input = Lütfen
21+
pass_rec_ = Hesabınız için hatırladığınız şifrenin bir benzerini giriniz...
22+
password_recovery_submit = Şifreyi kurtar
23+
password_recovery_report = Şifre kurtarma sonucu
24+
user_register_failed = Bu kullanıcı adı zaten alınmış!
25+
user_register_ip = Bu IP adresi zaten kayıtlı!
26+
register = Kayıt Ol
27+
user_register_failed_title = Kayıt başarısız!
28+
user_register_failed_description = Şifre boş olamaz.
29+
user_register_successfully = Kayıt başarılı!
30+
user_register_successfully_description = Artık giriş yapabilirsiniz.
31+
ollama_web_ui_title = Ollama uygulamanızda yüklü olan AI modellerinizi buradan kullanabilirsiniz
32+
user_model = Kullanmak istediğiniz modeli yazın. Örnek: llama3:latest
33+
model_question = Modelinize lütfen bir soru yöneltin.
34+
model_submit = Soru sor
35+
exit = Çıkış yap
36+
app_requirements = Giriş yapmanız gerekiyor!
37+
app_requirements_description = LXLM-Ollama-WebUI uygulamasını kullanabilmeniz için öncelikle giriş yapmanız gerekiyor.

user.ini

Whitespace-only changes.

webui.py

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
from flask import Flask, request
2+
from difflib import SequenceMatcher
3+
from ollama_model import *
4+
from lang import *
5+
6+
user = configparser.ConfigParser()
7+
user.read('user.ini')
8+
9+
app = Flask(__name__)
10+
11+
check = 0
12+
13+
def password_recovery_func(username, password, threshold=0.7):
14+
user_password = user[username]["password"]
15+
ratio = SequenceMatcher(None, user_password, password).ratio()
16+
if ratio >= threshold:
17+
password = user[username]["password"]
18+
return f"{your_password}: {password}"
19+
else:
20+
return f"{password_recovery_failed}"
21+
22+
@app.route('/', methods=['GET', 'POST'])
23+
def home():
24+
if request.method == 'POST':
25+
lang = request.form['lang']
26+
set_lang(lang)
27+
else:
28+
lang = "Language"
29+
global check
30+
check = 0
31+
return f"""
32+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
33+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
34+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
35+
<title>{app_title}</title>
36+
<h1 class="ms-2">{app_title}</h1>
37+
<form action="/" method="post">
38+
<select class="form-select form-select-sm position-absolute end-0 me-2" onchange="this.form.submit()" name="lang" style="top: 7px; width: 15%;">
39+
<option value="" disabled selected hidden>{lang}</option>
40+
<option value="TR">TR</option>
41+
<option value="EN">EN</option>
42+
</select>
43+
</form>
44+
<form action="/login" method="post">
45+
<input class="form-control form-control-sm w-75 ms-2" type="text" name="username" placeholder="{user_name_input}"/> <br>
46+
<input class="form-control form-control-sm w-75 ms-2" type="password" name="password" placeholder="{password_input}"/> <br>
47+
<input class="btn btn-primary ms-2" type="submit" value="{login_var}"/>
48+
</form>
49+
<a class="btn btn-primary ms-2" href="/user">{new_user_register}</a>
50+
"""
51+
52+
@app.route('/login', methods=['POST'])
53+
def login():
54+
global check
55+
username = request.form['username']
56+
password = request.form['password']
57+
58+
if username in user and user[username]["password"] == password:
59+
check = 1
60+
return f"""
61+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
62+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
63+
<title>{login_successfully}</title>
64+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
65+
<h1 class="ms-2">{login_successfully}</h1> <p class="ms-2">{welcome}, {username}</p>
66+
<a class="btn btn-primary ms-2" href="/app">{go_to_app}</a>
67+
"""
68+
else:
69+
username = request.form['username']
70+
return f"""
71+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
72+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
73+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
74+
<title>{login_failed}</title>
75+
<h1 class="ms-2">{login_failed}</h1> <p class="ms-2">{login_failed_description}</p>
76+
<form action="/passrecovery" method="post">
77+
<input type="hidden" name="username" value="{username}"/>
78+
<a class="btn btn-primary ms-2" href="/user">{user_register}</a><a class="btn btn-primary ms-2" href="/">{try_again}</a>
79+
<br>
80+
<input class="btn btn-primary ms-2 mt-2" type="submit" value="{password_forget}"/>
81+
</form>
82+
"""
83+
84+
@app.route('/passrecovery', methods=['GET', 'POST'])
85+
def pass_recovery():
86+
if request.method == 'POST':
87+
username = request.form['username']
88+
return f"""
89+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
90+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
91+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
92+
<title>{password_recovery}</title>
93+
<h1 class="ms-2">{password_recovery}</h1>
94+
<p class="ms-2">{username} {password_recovery_}</p>
95+
<form action="/recovery" method="post">
96+
<input type="hidden" name="username" value="{username}"/>
97+
<input class="form-control form-control-sm w-75 ms-2" type="password" name="password" placeholder="{password_recovery_input} {username} {pass_rec_}"/> <br>
98+
<input class="btn btn-primary ms-2" type="submit" value="{password_recovery_submit}"/>
99+
</form>
100+
"""
101+
@app.route('/recovery', methods=['GET', 'POST'])
102+
def recovery():
103+
if request.method == 'POST':
104+
username = request.form['username']
105+
password = request.form['password']
106+
return f"""
107+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
108+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
109+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
110+
<title>{password_recovery_report}</title>
111+
<h1 class="ms-2">{password_recovery_report}</h1>
112+
<p class="ms-2">{password_recovery_func(username, password)}</p>
113+
<a class="btn btn-primary ms-2" href="/">{try_again}</a>
114+
"""
115+
116+
@app.route('/user')
117+
def user_form():
118+
if request.method == 'POST':
119+
username = request.form['username']
120+
121+
if username in user:
122+
return f"""
123+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
124+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
125+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
126+
<title>{user_register_failed}</title>
127+
<h1>{user_register_failed}</h1>
128+
<a href="/user">{try_again}</a>
129+
"""
130+
131+
return f"""
132+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
133+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
134+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
135+
<title>{new_user_register}</title>
136+
<h1 class="ms-2">{new_user_register}</h1>
137+
<form action="/adduser" method="post">
138+
<input class="form-control form-control-sm w-75 ms-2" type="text" name="username" placeholder="{user_name_input}"/> <br>
139+
<input class="form-control form-control-sm w-75 ms-2" type="password" name="password" placeholder="{password_input}"/> <br>
140+
<input class="btn btn-primary ms-2" type="submit" value="{register}"/>
141+
</form>
142+
"""
143+
144+
@app.route('/adduser', methods=['POST'])
145+
def add_user():
146+
username = request.form['username']
147+
password = request.form['password']
148+
ip_address = request.remote_addr
149+
if user.has_section(f"{username}"):
150+
return f"""
151+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
152+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
153+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
154+
<h1 class="ms-2">{user_register_failed_title}</h1> <p class="ms-2">{user_register_failed}</p>
155+
<a class="btn btn-primary ms-2" href="/user">{try_again}</a>
156+
"""
157+
for section in user.sections():
158+
if user[section]["ip_address"] == ip_address:
159+
return f"""
160+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
161+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
162+
<title>{user_register_failed_title}</title>
163+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
164+
<h1 class="ms-2">{user_register_failed_title}</h1> <p class="ms-2">{user_register_ip}</p>
165+
<a class="btn btn-primary ms-2" href="/user">{try_again}</a>
166+
"""
167+
else:
168+
if password == "":
169+
return f"""
170+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
171+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
172+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
173+
<title>{user_register_failed_title}</title>
174+
<h1 class="ms-2">{user_register_failed_title}</h1> <p class="ms-2">{user_register_failed_description}</p>
175+
<a class="btn btn-primary ms-2" href="/user">{try_again}</a>
176+
"""
177+
user_ip = request.remote_addr
178+
user.add_section(f"{username}")
179+
user.set(f"{username}", "password", f"{password}")
180+
user.set(f"{username}", "ip_address", f"{user_ip}")
181+
with open("user.ini", "w") as configfile:
182+
user.write(configfile)
183+
return f"""
184+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
185+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
186+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
187+
<title>{user_register_successfully}</title>
188+
<h1 class="ms-2">{user_register_successfully}</h1> <p class="ms-2">{user_register_successfully_description}</p>
189+
<a class="btn btn-primary ms-2" href="/">{login_var}</a>
190+
"""
191+
192+
@app.route('/app', methods=['GET', 'POST'])
193+
def app_page():
194+
if check == 1:
195+
return f"""
196+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
197+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
198+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
199+
<title>{ollama_web_ui_title}</title>
200+
<h1 class="ms-2">{ollama_web_ui_title}</h1>
201+
<form action="/chat" method="post">
202+
<input class="form-control form-control-sm w-75 ms-2" type="text" name="model" placeholder="{user_model}"/>
203+
<input class="form-control form-control-sm w-75 ms-2 mt-2" type="text" name="msg" placeholder="{model_question}"/> <br>
204+
<input class="btn btn-primary ms-2" type="submit" value="{model_submit}"/>
205+
</form>
206+
<a class="btn btn-primary ms-2" href="/">{exit}</a>
207+
"""
208+
else:
209+
return f"""
210+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
211+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
212+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
213+
<title>{app_requirements}</title>
214+
<h1 class="ms-2">{app_requirements}</h1>
215+
<p class="ms-2">{app_requirements_description}</p>
216+
<a class="btn btn-primary ms-2" href="/">{login_var}</a>
217+
"""
218+
@app.route('/chat', methods=['GET', 'POST'])
219+
def chat():
220+
if request.method == "POST":
221+
model = request.form["model"]
222+
msg = request.form["msg"]
223+
user_response = f"{user_}: {msg}"
224+
model_response = load_model(model, msg)
225+
return f"""
226+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
227+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
228+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
229+
<title>{model}</title>
230+
<p class="alert alert-primary w-75 ms-2 mt-2">{user_response}</p>
231+
<p class="alert alert-success w-75 ms-2">{model}: {model_response}</p>
232+
<form action="/chat" method="post">
233+
<input type="hidden" name="model" value="{model}"/>
234+
<input class="form-control form-control-sm w-75 ms-2" type="text" name="msg" placeholder="{model_question}"/>
235+
<input class="btn btn-primary ms-2 mt-2" type="submit" value="{model_submit}"/>
236+
</form>
237+
<a class="btn btn-primary ms-2" href="/">{exit}</a>
238+
"""
239+
240+
if __name__ == '__main__':
241+
app.run(host="0.0.0.0", port=5000, debug=True)

0 commit comments

Comments
 (0)