dodanie
This commit is contained in:
parent
8be1d0a1c9
commit
f1a48d4acb
6
.env
Normal file
6
.env
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
SECRET_KEY=MOKO00koko
|
||||||
|
PASSWORD_KEY=tZnq1IyFSKeBwaLa0Bx5Ge722GgrJztHHNv3jqXMswo=
|
||||||
|
ADMIN_PASSWORD=admin
|
||||||
|
MODEL_REPO_URL=https:/git.pathl.pl
|
||||||
|
UPLOAD_LIMIT_MB=500
|
||||||
|
DEBUG=True
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
.venv/
|
|
||||||
.idea/
|
|
||||||
10
.idea/.gitignore
vendored
Normal file
10
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Ignored default folder with query files
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
10
.idea/Strona AI.iml
Normal file
10
.idea/Strona AI.iml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.13 (Strona AI)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
13
.idea/inspectionProfiles/Project_Default.xml
Normal file
13
.idea/inspectionProfiles/Project_Default.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredPackages">
|
||||||
|
<list>
|
||||||
|
<option value="torch" />
|
||||||
|
<option value="numpy" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
7
.idea/misc.xml
Normal file
7
.idea/misc.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.13 (Strona AI)" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (Strona AI)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/Strona AI.iml" filepath="$PROJECT_DIR$/.idea/Strona AI.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
144
app.py
Normal file
144
app.py
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
from flask import Flask, request, render_template, jsonify, send_from_directory
|
||||||
|
import os, json, importlib.util
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
import markdown
|
||||||
|
app = Flask(__name__)
|
||||||
|
UPLOAD_FOLDER = "models"
|
||||||
|
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
||||||
|
ADMIN_KEY = "supersecretadminkey"
|
||||||
|
|
||||||
|
MODELS = {}
|
||||||
|
|
||||||
|
def load_models():
|
||||||
|
for folder in os.listdir(UPLOAD_FOLDER):
|
||||||
|
path = os.path.join(UPLOAD_FOLDER, folder)
|
||||||
|
if not os.path.isdir(path):
|
||||||
|
continue
|
||||||
|
meta_path = os.path.join(path, "meta.json")
|
||||||
|
if not os.path.exists(meta_path):
|
||||||
|
continue
|
||||||
|
with open(meta_path) as f:
|
||||||
|
meta = json.load(f)
|
||||||
|
func_name = meta.get("function_name")
|
||||||
|
if not func_name:
|
||||||
|
continue
|
||||||
|
|
||||||
|
py_file = os.path.join(path, "model.py")
|
||||||
|
spec = importlib.util.spec_from_file_location("model_module", py_file)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
|
||||||
|
func = getattr(module, func_name)
|
||||||
|
MODELS[folder] = {
|
||||||
|
"func": func,
|
||||||
|
"inputs": meta.get("inputs", []),
|
||||||
|
"meta": meta,
|
||||||
|
"path": path
|
||||||
|
}
|
||||||
|
|
||||||
|
load_models()
|
||||||
|
|
||||||
|
# ---- DASHBOARD ----
|
||||||
|
@app.route("/")
|
||||||
|
def dashboard():
|
||||||
|
models_with_md = {}
|
||||||
|
for name, model in MODELS.items():
|
||||||
|
md_content = ""
|
||||||
|
try:
|
||||||
|
md_path = os.path.join(model["path"], model["meta"]["description_file"])
|
||||||
|
with open(md_path, "r", encoding="utf-8") as f:
|
||||||
|
md_text = f.read()
|
||||||
|
# konwersja Markdown -> HTML
|
||||||
|
md_content = markdown.markdown(md_text)
|
||||||
|
except Exception:
|
||||||
|
md_content = "<p>Brak opisu.</p>"
|
||||||
|
|
||||||
|
models_with_md[name] = {**model, "md_content": md_content}
|
||||||
|
|
||||||
|
return render_template("user.html", models=models_with_md)
|
||||||
|
# ---- PREDICT ----
|
||||||
|
@app.route("/predict/<model_name>", methods=["POST"])
|
||||||
|
def predict(model_name):
|
||||||
|
model_entry = MODELS.get(model_name)
|
||||||
|
if not model_entry:
|
||||||
|
return jsonify({"output": "Model nie istnieje"}), 404
|
||||||
|
|
||||||
|
func = model_entry["func"]
|
||||||
|
inputs = model_entry["inputs"]
|
||||||
|
kwargs = {}
|
||||||
|
for inp in inputs:
|
||||||
|
kwargs[inp] = request.form.get(inp)
|
||||||
|
|
||||||
|
try:
|
||||||
|
for k in kwargs:
|
||||||
|
try:
|
||||||
|
kwargs[k] = float(kwargs[k])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
output = func(**kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
output = str(e)
|
||||||
|
|
||||||
|
return jsonify({"output": str(output)})
|
||||||
|
|
||||||
|
# ---- ADD MODEL ----
|
||||||
|
@app.route("/add_model", methods=["POST"])
|
||||||
|
def add_model():
|
||||||
|
admin_key = request.form.get("admin_key")
|
||||||
|
if admin_key != ADMIN_KEY:
|
||||||
|
return "Brak dostępu", 403
|
||||||
|
|
||||||
|
name = request.form["name"]
|
||||||
|
function_name = request.form["function_name"]
|
||||||
|
inputs = request.form.getlist("inputs")
|
||||||
|
model_py = request.files["model_py"]
|
||||||
|
description_file = request.files["description"]
|
||||||
|
|
||||||
|
folder_name = secure_filename(name)
|
||||||
|
folder_path = os.path.join(UPLOAD_FOLDER, folder_name)
|
||||||
|
os.makedirs(folder_path, exist_ok=True)
|
||||||
|
|
||||||
|
# zapis plików
|
||||||
|
model_py.save(os.path.join(folder_path, "model.py"))
|
||||||
|
description_file.save(os.path.join(folder_path, "description.md"))
|
||||||
|
|
||||||
|
# meta.json
|
||||||
|
meta = {
|
||||||
|
"name": name,
|
||||||
|
"function_name": function_name,
|
||||||
|
"inputs": inputs,
|
||||||
|
"description_file": "description.md",
|
||||||
|
"downloadable": True, # można pobrać pliki
|
||||||
|
"type": request.form.get("type", "algorithmic")
|
||||||
|
}
|
||||||
|
with open(os.path.join(folder_path, "meta.json"), "w") as f:
|
||||||
|
json.dump(meta, f)
|
||||||
|
|
||||||
|
# załaduj od razu
|
||||||
|
spec = importlib.util.spec_from_file_location("model_module", os.path.join(folder_path, "model.py"))
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
func = getattr(module, function_name)
|
||||||
|
|
||||||
|
MODELS[folder_name] = {
|
||||||
|
"func": func,
|
||||||
|
"inputs": inputs,
|
||||||
|
"meta": meta,
|
||||||
|
"path": folder_path
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Dodano model!"
|
||||||
|
|
||||||
|
# ---- DOWNLOAD FILE ----
|
||||||
|
@app.route("/download/<model_name>/<file_name>")
|
||||||
|
def download_file(model_name, file_name):
|
||||||
|
model_entry = MODELS.get(model_name)
|
||||||
|
if not model_entry:
|
||||||
|
return "Model nie istnieje", 404
|
||||||
|
folder_path = model_entry["path"]
|
||||||
|
if file_name not in ["model.py", "description.md", "meta.json"]:
|
||||||
|
return "Nieprawidłowy plik", 400
|
||||||
|
return send_from_directory(folder_path, file_name, as_attachment=True)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(debug=True)
|
||||||
22
main.py
22
main.py
@ -1,22 +0,0 @@
|
|||||||
from flask import Flask, request, jsonify
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
|
|
||||||
# Strona główna
|
|
||||||
@app.route("/")
|
|
||||||
def home():
|
|
||||||
return "AI Pathl Test działa!"
|
|
||||||
|
|
||||||
# Endpoint testowy np. dla promptów AI
|
|
||||||
@app.route("/test", methods=["POST"])
|
|
||||||
def test_ai():
|
|
||||||
data = request.json
|
|
||||||
prompt = data.get("prompt", "")
|
|
||||||
# Na razie tylko echo promptu
|
|
||||||
response = {"response": f"Otrzymałem Twój prompt: {prompt}"}
|
|
||||||
return jsonify(response)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("server")
|
|
||||||
print("dodanie")
|
|
||||||
app.run(host="0.0.0.0", port=6000)
|
|
||||||
1
models/Power/Readme.md
Normal file
1
models/Power/Readme.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
#model Power
|
||||||
BIN
models/Power/__pycache__/model.cpython-313.pyc
Normal file
BIN
models/Power/__pycache__/model.cpython-313.pyc
Normal file
Binary file not shown.
18
models/Power/description.md
Normal file
18
models/Power/description.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Model Power
|
||||||
|
|
||||||
|
Ten model oblicza potęgę liczby.
|
||||||
|
|
||||||
|
## Funkcje:
|
||||||
|
|
||||||
|
- `oblicz_potega(x, typ)`
|
||||||
|
|
||||||
|
### Parametry:
|
||||||
|
|
||||||
|
- `x` (int/float): liczba do podniesienia do potęgi
|
||||||
|
- `typ` (str): `"kwadrat"` lub `"sześcian"`
|
||||||
|
|
||||||
|
### Przykłady użycia:
|
||||||
|
|
||||||
|
```python
|
||||||
|
oblicz_potega(3, "kwadrat") # wynik: 9
|
||||||
|
oblicz_potega(2, "sześcian") # wynik: 8
|
||||||
6
models/Power/meta.json
Normal file
6
models/Power/meta.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "Power",
|
||||||
|
"function_name": "oblicz_potega",
|
||||||
|
"inputs": ["x","typ"],
|
||||||
|
"description_file": "description.md"
|
||||||
|
}
|
||||||
16
models/Power/model.py
Normal file
16
models/Power/model.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
def oblicz_potega(x, typ):
|
||||||
|
"""
|
||||||
|
Funkcja oblicza kwadrat lub sześcian liczby x w zależności od parametru typ.
|
||||||
|
Parametry:
|
||||||
|
x (int/float): liczba, którą chcemy podnieść do potęgi
|
||||||
|
typ (str): "kwadrat" lub "sześcian"
|
||||||
|
|
||||||
|
Zwraca:
|
||||||
|
int/float: wynik działania
|
||||||
|
"""
|
||||||
|
if typ == "kwadrat":
|
||||||
|
return x ** 2
|
||||||
|
elif typ == "sześcian":
|
||||||
|
return x ** 3
|
||||||
|
else:
|
||||||
|
return "Niepoprawny typ. Wpisz 'kwadrat' lub 'sześcian'."
|
||||||
150
static/style.css
Normal file
150
static/style.css
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/* Reset podstawowy */
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Body */
|
||||||
|
body {
|
||||||
|
background: #f4f7fc;
|
||||||
|
color: #333;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nagłówek */
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
color: #4a4a4a;
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kontener modeli */
|
||||||
|
#models-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Karta modelu */
|
||||||
|
.model-card {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 6px 18px rgba(0,0,0,0.08);
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 12px 24px rgba(0,0,0,0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nazwa modelu */
|
||||||
|
.model-card h3 {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
color: #1a1a1a;
|
||||||
|
font-size: 1.4em;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Opis modelu (.md) */
|
||||||
|
.description {
|
||||||
|
font-style: italic;
|
||||||
|
color: #555;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Formularz */
|
||||||
|
form.predict-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.predict-form label {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.predict-form input {
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
font-size: 0.95em;
|
||||||
|
transition: border 0.2s, box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.predict-form input:focus {
|
||||||
|
border-color: #6c63ff;
|
||||||
|
box-shadow: 0 0 5px rgba(108, 99, 255, 0.4);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Przycisk */
|
||||||
|
form.predict-form button {
|
||||||
|
padding: 10px 15px;
|
||||||
|
background: #6c63ff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: background 0.2s, transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.predict-form button:hover {
|
||||||
|
background: #574fd6;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Output */
|
||||||
|
.output {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1a73e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Download linki */
|
||||||
|
.download {
|
||||||
|
margin-top: 15px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #6c63ff;
|
||||||
|
margin-right: 10px;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download a:hover {
|
||||||
|
color: #574fd6;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Linie oddzielające karty */
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
height: 1px;
|
||||||
|
background: #e0e0e0;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsywność */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
h2 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.predict-form input, form.predict-form button {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
templates/user.html
Normal file
59
templates/user.html
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>AI Models</title>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>Lista modeli AI</h2>
|
||||||
|
<div id="models-container">
|
||||||
|
{% for name, model in models.items() %}
|
||||||
|
<div class="model-card" id="model-{{name}}">
|
||||||
|
<h3>{{name}}</h3>
|
||||||
|
<div class="description">
|
||||||
|
{{ model.md_content | safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<form class="predict-form">
|
||||||
|
{% for inp in model.inputs %}
|
||||||
|
<label>{{inp}}:</label>
|
||||||
|
<input name="{{inp}}"><br>
|
||||||
|
{% endfor %}
|
||||||
|
<button type="submit">Wyślij</button>
|
||||||
|
</form>
|
||||||
|
<div class="output" id="output-{{name}}"></div>
|
||||||
|
{% if model.meta.downloadable %}
|
||||||
|
<div class="download">
|
||||||
|
<a href="/download/{{name}}/model.py" target="_blank">Pobierz model.py</a> |
|
||||||
|
<a href="/download/{{name}}/description.md" target="_blank">Pobierz description.md</a> |
|
||||||
|
<a href="/download/{{name}}/meta.json" target="_blank">Pobierz meta.json</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.querySelectorAll(".predict-form").forEach(form => {
|
||||||
|
form.addEventListener("submit", async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const parent = e.target.closest(".model-card");
|
||||||
|
const modelName = parent.id.replace("model-", "");
|
||||||
|
const outputDiv = parent.querySelector(".output");
|
||||||
|
|
||||||
|
const formData = new FormData(form);
|
||||||
|
const response = await fetch(`/predict/${modelName}`, {
|
||||||
|
method: "POST",
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
outputDiv.textContent = data.output;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Reference in New Issue
Block a user