This commit is contained in:
ALicja 2026-01-30 13:33:51 +01:00
parent 8be1d0a1c9
commit f1a48d4acb
20 changed files with 463 additions and 24 deletions

6
.env Normal file
View 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
View File

@ -1,2 +0,0 @@
.venv/
.idea/

10
.idea/.gitignore vendored Normal file
View 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
View 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>

View 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>

View 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
View 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
View 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
View 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>

View File

@ -0,0 +1,3 @@
#Pathl.Ai cześc projektu Cafe
Ceo: Maro

144
app.py Normal file
View 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
View File

@ -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)

0
model.py Normal file
View File

1
models/Power/Readme.md Normal file
View File

@ -0,0 +1 @@
#model Power

Binary file not shown.

View 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
View 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
View 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
View 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
View 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>