Spaces:
Running on Zero
Running on Zero
Commit Β·
77105ca
1
Parent(s): 183208c
Inmemory
Browse files- .dockerignore +12 -0
- Dockerfile +32 -0
- app.py +3 -6
- config.py +0 -3
- db/store.py +15 -75
- docker-compose.yml +19 -0
- ui/compress_tab.py +32 -29
- ui/history_tab.py +27 -18
.dockerignore
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.git
|
| 2 |
+
.venv
|
| 3 |
+
.env
|
| 4 |
+
__pycache__
|
| 5 |
+
*.pyc
|
| 6 |
+
*.db
|
| 7 |
+
.claude
|
| 8 |
+
CLAUDE.md
|
| 9 |
+
AGENTS.md
|
| 10 |
+
my-notes
|
| 11 |
+
docs
|
| 12 |
+
*.ipynb
|
Dockerfile
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.12-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# Install system dependencies required by torch and transformers
|
| 6 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 7 |
+
build-essential \
|
| 8 |
+
git \
|
| 9 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 10 |
+
|
| 11 |
+
# Install CPU-only torch first to keep image lean, then remaining deps
|
| 12 |
+
COPY requirements.txt .
|
| 13 |
+
RUN pip install --no-cache-dir torch>=2.2.0 --index-url https://download.pytorch.org/whl/cpu \
|
| 14 |
+
&& pip install --no-cache-dir -r requirements.txt
|
| 15 |
+
|
| 16 |
+
# Copy application code
|
| 17 |
+
COPY app.py config.py ./
|
| 18 |
+
COPY core/ core/
|
| 19 |
+
COPY db/ db/
|
| 20 |
+
COPY models/ models/
|
| 21 |
+
COPY ui/ ui/
|
| 22 |
+
|
| 23 |
+
# HuggingFace model cache is stored under /data so it can be mounted as a
|
| 24 |
+
# volume and downloaded weights survive container restarts.
|
| 25 |
+
ENV HF_HOME=/data/hf_cache \
|
| 26 |
+
PORT=7860
|
| 27 |
+
|
| 28 |
+
VOLUME /data
|
| 29 |
+
|
| 30 |
+
EXPOSE 7860
|
| 31 |
+
|
| 32 |
+
CMD ["python", "app.py"]
|
app.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import config
|
| 3 |
-
from db.store import init_db
|
| 4 |
from models.model_loader import get_llm, get_embedder
|
| 5 |
from ui.compress_tab import build_compress_tab
|
| 6 |
from ui.history_tab import build_history_tab
|
|
@@ -8,15 +7,13 @@ from ui.history_tab import build_history_tab
|
|
| 8 |
|
| 9 |
def build_app() -> gr.Blocks:
|
| 10 |
with gr.Blocks(title=config.APP_TITLE) as app:
|
| 11 |
-
|
| 12 |
-
|
|
|
|
| 13 |
return app
|
| 14 |
|
| 15 |
|
| 16 |
if __name__ == "__main__":
|
| 17 |
-
print("Initialising database...")
|
| 18 |
-
init_db()
|
| 19 |
-
|
| 20 |
print("Loading models (first run may download weights)...")
|
| 21 |
get_llm()
|
| 22 |
get_embedder()
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import config
|
|
|
|
| 3 |
from models.model_loader import get_llm, get_embedder
|
| 4 |
from ui.compress_tab import build_compress_tab
|
| 5 |
from ui.history_tab import build_history_tab
|
|
|
|
| 7 |
|
| 8 |
def build_app() -> gr.Blocks:
|
| 9 |
with gr.Blocks(title=config.APP_TITLE) as app:
|
| 10 |
+
run_store = gr.State(value=[])
|
| 11 |
+
build_compress_tab(run_store)
|
| 12 |
+
build_history_tab(run_store)
|
| 13 |
return app
|
| 14 |
|
| 15 |
|
| 16 |
if __name__ == "__main__":
|
|
|
|
|
|
|
|
|
|
| 17 |
print("Loading models (first run may download weights)...")
|
| 18 |
get_llm()
|
| 19 |
get_embedder()
|
config.py
CHANGED
|
@@ -60,9 +60,6 @@ EMBEDDER_INFO = {
|
|
| 60 |
DEFAULT_TARGET_TOKENS = 500
|
| 61 |
MAX_NEW_TOKENS = 1024
|
| 62 |
|
| 63 |
-
# Database
|
| 64 |
-
DB_PATH = os.getenv("DB_PATH", "tinypress.db")
|
| 65 |
-
|
| 66 |
# Gradio
|
| 67 |
APP_TITLE = "TinyPress"
|
| 68 |
SERVER_PORT = int(os.getenv("PORT", 7860))
|
|
|
|
| 60 |
DEFAULT_TARGET_TOKENS = 500
|
| 61 |
MAX_NEW_TOKENS = 1024
|
| 62 |
|
|
|
|
|
|
|
|
|
|
| 63 |
# Gradio
|
| 64 |
APP_TITLE = "TinyPress"
|
| 65 |
SERVER_PORT = int(os.getenv("PORT", 7860))
|
db/store.py
CHANGED
|
@@ -1,87 +1,27 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
from pathlib import Path
|
| 4 |
|
| 5 |
|
| 6 |
-
def
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
return conn
|
| 10 |
|
| 11 |
|
| 12 |
-
def
|
| 13 |
-
|
| 14 |
-
conn = _connect()
|
| 15 |
-
conn.executescript(schema.read_text())
|
| 16 |
-
# Migrate existing databases that pre-date new columns.
|
| 17 |
-
for col, typedef in [("tokenizer", "TEXT NOT NULL DEFAULT ''"), ("duration_ms", "REAL NOT NULL DEFAULT 0"), ("feedback", "INTEGER"), ("feedback_comment", "TEXT")]:
|
| 18 |
-
try:
|
| 19 |
-
conn.execute(f"ALTER TABLE compression_runs ADD COLUMN {col} {typedef}")
|
| 20 |
-
except sqlite3.OperationalError:
|
| 21 |
-
pass # column already exists
|
| 22 |
-
conn.commit()
|
| 23 |
-
conn.close()
|
| 24 |
|
| 25 |
|
| 26 |
-
def
|
| 27 |
-
|
| 28 |
-
cursor = conn.execute(
|
| 29 |
-
"""
|
| 30 |
-
INSERT INTO compression_runs
|
| 31 |
-
(timestamp, model, tokenizer, input_tokens, output_tokens, target_tokens,
|
| 32 |
-
compression_ratio, quality_score, duration_ms, input_text, output_text)
|
| 33 |
-
VALUES
|
| 34 |
-
(:timestamp, :model, :tokenizer, :input_tokens, :output_tokens, :target_tokens,
|
| 35 |
-
:compression_ratio, :quality_score, :duration_ms, :input_text, :output_text)
|
| 36 |
-
""",
|
| 37 |
-
record,
|
| 38 |
-
)
|
| 39 |
-
run_id = cursor.lastrowid
|
| 40 |
-
conn.commit()
|
| 41 |
-
conn.close()
|
| 42 |
-
return run_id
|
| 43 |
|
| 44 |
|
| 45 |
-
def
|
| 46 |
-
|
| 47 |
-
conn.execute(
|
| 48 |
-
"UPDATE compression_runs SET feedback = ? WHERE id = ?",
|
| 49 |
-
(value, run_id),
|
| 50 |
-
)
|
| 51 |
-
conn.commit()
|
| 52 |
-
conn.close()
|
| 53 |
|
| 54 |
|
| 55 |
-
def
|
| 56 |
-
|
| 57 |
-
conn.execute(
|
| 58 |
-
"UPDATE compression_runs SET feedback_comment = ? WHERE id = ?",
|
| 59 |
-
(comment, run_id),
|
| 60 |
-
)
|
| 61 |
-
conn.commit()
|
| 62 |
-
conn.close()
|
| 63 |
|
| 64 |
|
| 65 |
-
def
|
| 66 |
-
|
| 67 |
-
conn.execute("DELETE FROM compression_runs WHERE id = ?", (run_id,))
|
| 68 |
-
conn.commit()
|
| 69 |
-
conn.close()
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
def get_run(run_id: int) -> dict | None:
|
| 73 |
-
conn = _connect()
|
| 74 |
-
row = conn.execute(
|
| 75 |
-
"SELECT * FROM compression_runs WHERE id = ?", (run_id,)
|
| 76 |
-
).fetchone()
|
| 77 |
-
conn.close()
|
| 78 |
-
return dict(row) if row else None
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
def get_runs(limit: int = 100) -> list[dict]:
|
| 82 |
-
conn = _connect()
|
| 83 |
-
rows = conn.execute(
|
| 84 |
-
"SELECT * FROM compression_runs ORDER BY id DESC LIMIT ?", (limit,)
|
| 85 |
-
).fetchall()
|
| 86 |
-
conn.close()
|
| 87 |
-
return [dict(r) for r in rows]
|
|
|
|
| 1 |
+
def make_store() -> list:
|
| 2 |
+
return []
|
|
|
|
| 3 |
|
| 4 |
|
| 5 |
+
def save_run(store: list, record: dict) -> tuple[int, list]:
|
| 6 |
+
run_id = store[-1]["id"] + 1 if store else 1
|
| 7 |
+
return run_id, store + [{"id": run_id, **record}]
|
|
|
|
| 8 |
|
| 9 |
|
| 10 |
+
def get_runs(store: list, limit: int = 100) -> list[dict]:
|
| 11 |
+
return list(reversed(store))[:limit]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
|
| 14 |
+
def get_run(store: list, run_id: int) -> dict | None:
|
| 15 |
+
return next((r for r in store if r["id"] == run_id), None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
|
| 18 |
+
def delete_run(store: list, run_id: int) -> list:
|
| 19 |
+
return [r for r in store if r["id"] != run_id]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
|
| 22 |
+
def update_feedback(store: list, run_id: int, value: int) -> list:
|
| 23 |
+
return [{**r, "feedback": value} if r["id"] == run_id else r for r in store]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
|
| 26 |
+
def update_feedback_comment(store: list, run_id: int, comment: str) -> list:
|
| 27 |
+
return [{**r, "feedback_comment": comment} if r["id"] == run_id else r for r in store]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docker-compose.yml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
networks:
|
| 2 |
+
hf-build-small:
|
| 3 |
+
driver: bridge
|
| 4 |
+
|
| 5 |
+
volumes:
|
| 6 |
+
tinypress-data:
|
| 7 |
+
|
| 8 |
+
services:
|
| 9 |
+
tinypress:
|
| 10 |
+
build: .
|
| 11 |
+
ports:
|
| 12 |
+
- "7860:7860"
|
| 13 |
+
volumes:
|
| 14 |
+
- tinypress-data:/data
|
| 15 |
+
env_file:
|
| 16 |
+
- .env
|
| 17 |
+
networks:
|
| 18 |
+
- hf-build-small
|
| 19 |
+
restart: unless-stopped
|
ui/compress_tab.py
CHANGED
|
@@ -42,7 +42,6 @@ def _render_token_html(text: str) -> str:
|
|
| 42 |
spans = []
|
| 43 |
for i, tok in enumerate(tokens):
|
| 44 |
color = _PALETTE[i % len(_PALETTE)]
|
| 45 |
-
# Make leading whitespace visible with a mid-dot; escape everything else.
|
| 46 |
display = _h.escape(tok).replace(
|
| 47 |
" ", '<span style="opacity:0.35;font-size:0.7em">Β·</span>'
|
| 48 |
)
|
|
@@ -112,12 +111,13 @@ def compression_status(text: str, target_tokens: int) -> str:
|
|
| 112 |
|
| 113 |
# ββ core handlers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 114 |
|
| 115 |
-
def run_compression(text: str, target_tokens: int):
|
| 116 |
_hidden = gr.update(visible=False)
|
| 117 |
if not text.strip():
|
| 118 |
return ("", 0, 0, 0, 0.0, None,
|
| 119 |
_hidden, _hidden, gr.update(value="", visible=False),
|
| 120 |
-
gr.update(value="", visible=False), _hidden, gr.update(value="", visible=False)
|
|
|
|
| 121 |
|
| 122 |
t0 = time.perf_counter()
|
| 123 |
compressed, input_tokens, output_tokens = compress(text, int(target_tokens))
|
|
@@ -126,7 +126,7 @@ def run_compression(text: str, target_tokens: int):
|
|
| 126 |
ratio = round(output_tokens / input_tokens, 4) if input_tokens else 0.0
|
| 127 |
quality = semantic_score(text, compressed)
|
| 128 |
|
| 129 |
-
run_id = save_run({
|
| 130 |
"timestamp": datetime.now(timezone.utc).isoformat(),
|
| 131 |
"model": get_current_model_id() or config.LLM_MODEL,
|
| 132 |
"tokenizer": get_current_tokenizer_id() or config.LLM_MODEL,
|
|
@@ -143,11 +143,12 @@ def run_compression(text: str, target_tokens: int):
|
|
| 143 |
return (
|
| 144 |
compressed, input_tokens, output_tokens, ratio, quality,
|
| 145 |
run_id,
|
| 146 |
-
gr.update(visible=True), gr.update(visible=True),
|
| 147 |
-
gr.update(value="", visible=True),
|
| 148 |
-
gr.update(value="", visible=False),
|
| 149 |
-
gr.update(visible=False),
|
| 150 |
-
gr.update(value="", visible=False),
|
|
|
|
| 151 |
)
|
| 152 |
|
| 153 |
|
|
@@ -173,26 +174,28 @@ def on_embedder_change(model_id: str) -> str:
|
|
| 173 |
return config.EMBEDDER_INFO.get(model_id, "")
|
| 174 |
|
| 175 |
|
| 176 |
-
def submit_feedback(run_id, value: int):
|
| 177 |
if run_id is None:
|
| 178 |
-
return "Run a compression first.",
|
| 179 |
-
|
|
|
|
|
|
|
| 180 |
msg = "π Marked as helpful β thanks!" if value == 1 else "π Noted β thanks for the feedback!"
|
| 181 |
-
return msg, gr.update(visible=True), gr.update(visible=True), gr.update(value="", visible=False)
|
| 182 |
|
| 183 |
|
| 184 |
-
def save_comment(run_id, comment: str):
|
| 185 |
if run_id is None:
|
| 186 |
-
return gr.update(value="Run a compression first.", visible=True)
|
| 187 |
if not comment.strip():
|
| 188 |
-
return gr.update(value="Type a note first.", visible=True)
|
| 189 |
-
update_feedback_comment(run_id, comment.strip())
|
| 190 |
-
return gr.update(value="β Note saved.", visible=True)
|
| 191 |
|
| 192 |
|
| 193 |
# ββ UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 194 |
|
| 195 |
-
def build_compress_tab() -> gr.Tab:
|
| 196 |
with gr.Tab("Compress") as tab:
|
| 197 |
gr.Markdown("## TinyPress β Prompt Compression Engine")
|
| 198 |
gr.Markdown(
|
|
@@ -303,25 +306,25 @@ def build_compress_tab() -> gr.Tab:
|
|
| 303 |
load_embedder_btn.click(fn=load_embedder, inputs=[embedder_dropdown], outputs=[embedder_status])
|
| 304 |
compress_btn.click(
|
| 305 |
fn=run_compression,
|
| 306 |
-
inputs=[input_text, target_slider],
|
| 307 |
outputs=[output_text, input_tok, output_tok, ratio, quality,
|
| 308 |
last_run_id, thumbs_up_btn, thumbs_down_btn, feedback_status,
|
| 309 |
-
comment_box, save_comment_btn, comment_saved],
|
| 310 |
)
|
| 311 |
thumbs_up_btn.click(
|
| 312 |
-
fn=lambda run_id: submit_feedback(run_id, 1),
|
| 313 |
-
inputs=[last_run_id],
|
| 314 |
-
outputs=[feedback_status, comment_box, save_comment_btn, comment_saved],
|
| 315 |
)
|
| 316 |
thumbs_down_btn.click(
|
| 317 |
-
fn=lambda run_id: submit_feedback(run_id, -1),
|
| 318 |
-
inputs=[last_run_id],
|
| 319 |
-
outputs=[feedback_status, comment_box, save_comment_btn, comment_saved],
|
| 320 |
)
|
| 321 |
save_comment_btn.click(
|
| 322 |
fn=save_comment,
|
| 323 |
-
inputs=[last_run_id, comment_box],
|
| 324 |
-
outputs=[comment_saved],
|
| 325 |
)
|
| 326 |
|
| 327 |
return tab
|
|
|
|
| 42 |
spans = []
|
| 43 |
for i, tok in enumerate(tokens):
|
| 44 |
color = _PALETTE[i % len(_PALETTE)]
|
|
|
|
| 45 |
display = _h.escape(tok).replace(
|
| 46 |
" ", '<span style="opacity:0.35;font-size:0.7em">Β·</span>'
|
| 47 |
)
|
|
|
|
| 111 |
|
| 112 |
# ββ core handlers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 113 |
|
| 114 |
+
def run_compression(text: str, target_tokens: int, run_store: list):
|
| 115 |
_hidden = gr.update(visible=False)
|
| 116 |
if not text.strip():
|
| 117 |
return ("", 0, 0, 0, 0.0, None,
|
| 118 |
_hidden, _hidden, gr.update(value="", visible=False),
|
| 119 |
+
gr.update(value="", visible=False), _hidden, gr.update(value="", visible=False),
|
| 120 |
+
run_store)
|
| 121 |
|
| 122 |
t0 = time.perf_counter()
|
| 123 |
compressed, input_tokens, output_tokens = compress(text, int(target_tokens))
|
|
|
|
| 126 |
ratio = round(output_tokens / input_tokens, 4) if input_tokens else 0.0
|
| 127 |
quality = semantic_score(text, compressed)
|
| 128 |
|
| 129 |
+
run_id, new_store = save_run(run_store, {
|
| 130 |
"timestamp": datetime.now(timezone.utc).isoformat(),
|
| 131 |
"model": get_current_model_id() or config.LLM_MODEL,
|
| 132 |
"tokenizer": get_current_tokenizer_id() or config.LLM_MODEL,
|
|
|
|
| 143 |
return (
|
| 144 |
compressed, input_tokens, output_tokens, ratio, quality,
|
| 145 |
run_id,
|
| 146 |
+
gr.update(visible=True), gr.update(visible=True),
|
| 147 |
+
gr.update(value="", visible=True),
|
| 148 |
+
gr.update(value="", visible=False),
|
| 149 |
+
gr.update(visible=False),
|
| 150 |
+
gr.update(value="", visible=False),
|
| 151 |
+
new_store,
|
| 152 |
)
|
| 153 |
|
| 154 |
|
|
|
|
| 174 |
return config.EMBEDDER_INFO.get(model_id, "")
|
| 175 |
|
| 176 |
|
| 177 |
+
def submit_feedback(run_id, value: int, run_store: list):
|
| 178 |
if run_id is None:
|
| 179 |
+
return ("Run a compression first.",
|
| 180 |
+
gr.update(visible=False), gr.update(visible=False),
|
| 181 |
+
gr.update(value="", visible=False), run_store)
|
| 182 |
+
new_store = update_feedback(run_store, run_id, value)
|
| 183 |
msg = "π Marked as helpful β thanks!" if value == 1 else "π Noted β thanks for the feedback!"
|
| 184 |
+
return msg, gr.update(visible=True), gr.update(visible=True), gr.update(value="", visible=False), new_store
|
| 185 |
|
| 186 |
|
| 187 |
+
def save_comment(run_id, comment: str, run_store: list):
|
| 188 |
if run_id is None:
|
| 189 |
+
return gr.update(value="Run a compression first.", visible=True), run_store
|
| 190 |
if not comment.strip():
|
| 191 |
+
return gr.update(value="Type a note first.", visible=True), run_store
|
| 192 |
+
new_store = update_feedback_comment(run_store, run_id, comment.strip())
|
| 193 |
+
return gr.update(value="β Note saved.", visible=True), new_store
|
| 194 |
|
| 195 |
|
| 196 |
# ββ UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 197 |
|
| 198 |
+
def build_compress_tab(run_store) -> gr.Tab:
|
| 199 |
with gr.Tab("Compress") as tab:
|
| 200 |
gr.Markdown("## TinyPress β Prompt Compression Engine")
|
| 201 |
gr.Markdown(
|
|
|
|
| 306 |
load_embedder_btn.click(fn=load_embedder, inputs=[embedder_dropdown], outputs=[embedder_status])
|
| 307 |
compress_btn.click(
|
| 308 |
fn=run_compression,
|
| 309 |
+
inputs=[input_text, target_slider, run_store],
|
| 310 |
outputs=[output_text, input_tok, output_tok, ratio, quality,
|
| 311 |
last_run_id, thumbs_up_btn, thumbs_down_btn, feedback_status,
|
| 312 |
+
comment_box, save_comment_btn, comment_saved, run_store],
|
| 313 |
)
|
| 314 |
thumbs_up_btn.click(
|
| 315 |
+
fn=lambda run_id, store: submit_feedback(run_id, 1, store),
|
| 316 |
+
inputs=[last_run_id, run_store],
|
| 317 |
+
outputs=[feedback_status, comment_box, save_comment_btn, comment_saved, run_store],
|
| 318 |
)
|
| 319 |
thumbs_down_btn.click(
|
| 320 |
+
fn=lambda run_id, store: submit_feedback(run_id, -1, store),
|
| 321 |
+
inputs=[last_run_id, run_store],
|
| 322 |
+
outputs=[feedback_status, comment_box, save_comment_btn, comment_saved, run_store],
|
| 323 |
)
|
| 324 |
save_comment_btn.click(
|
| 325 |
fn=save_comment,
|
| 326 |
+
inputs=[last_run_id, comment_box, run_store],
|
| 327 |
+
outputs=[comment_saved, run_store],
|
| 328 |
)
|
| 329 |
|
| 330 |
return tab
|
ui/history_tab.py
CHANGED
|
@@ -11,10 +11,18 @@ _ALL_COLS = [
|
|
| 11 |
"feedback", "feedback_comment",
|
| 12 |
]
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
-
|
|
|
|
| 16 |
cols = selected_cols if selected_cols else _DEFAULT_COLS
|
| 17 |
-
runs = get_runs(limit=100)
|
| 18 |
if not runs:
|
| 19 |
return pd.DataFrame(columns=cols), "", "", ""
|
| 20 |
df = pd.DataFrame(runs)
|
|
@@ -25,30 +33,31 @@ def load_history(selected_cols=None):
|
|
| 25 |
return df, avg_quality, avg_ratio, ""
|
| 26 |
|
| 27 |
|
| 28 |
-
def on_row_select(evt: gr.SelectData, df: pd.DataFrame):
|
| 29 |
if df is None or df.empty:
|
| 30 |
return None, "", "No rows available."
|
| 31 |
row_idx = evt.index[0]
|
| 32 |
run_id = int(df.iloc[row_idx]["id"])
|
| 33 |
-
record = get_run(run_id)
|
| 34 |
if not record:
|
| 35 |
-
return None, "", f"Row {run_id} not found
|
| 36 |
diff_html = render_diff_html(record)
|
| 37 |
return run_id, diff_html, f"Row {run_id} selected β click Delete to remove."
|
| 38 |
|
| 39 |
|
| 40 |
-
def delete_selected(run_id, selected_cols):
|
| 41 |
if run_id is None:
|
| 42 |
-
df, avg_q, avg_r, _ = load_history(selected_cols)
|
| 43 |
-
return df, avg_q, avg_r, None, "", "No row selected."
|
| 44 |
-
delete_run(run_id)
|
| 45 |
-
df, avg_q, avg_r, _ = load_history(selected_cols)
|
| 46 |
-
return df, avg_q, avg_r, None, "", f"Row {run_id} deleted."
|
| 47 |
|
| 48 |
|
| 49 |
-
def build_history_tab() -> gr.Tab:
|
| 50 |
with gr.Tab("History") as tab:
|
| 51 |
gr.Markdown("## Compression Run History")
|
|
|
|
| 52 |
|
| 53 |
with gr.Row():
|
| 54 |
refresh_btn = gr.Button("Refresh", variant="secondary")
|
|
@@ -79,18 +88,18 @@ def build_history_tab() -> gr.Tab:
|
|
| 79 |
|
| 80 |
_outputs = [history_table, avg_quality, avg_ratio, diff_panel]
|
| 81 |
|
| 82 |
-
refresh_btn.click(fn=load_history, inputs=[col_picker], outputs=_outputs)
|
| 83 |
-
tab.select(fn=load_history, inputs=[col_picker], outputs=_outputs)
|
| 84 |
-
col_picker.change(fn=load_history, inputs=[col_picker], outputs=_outputs)
|
| 85 |
history_table.select(
|
| 86 |
fn=on_row_select,
|
| 87 |
-
inputs=[history_table],
|
| 88 |
outputs=[selected_id, diff_panel, delete_status],
|
| 89 |
)
|
| 90 |
delete_btn.click(
|
| 91 |
fn=delete_selected,
|
| 92 |
-
inputs=[selected_id, col_picker],
|
| 93 |
-
outputs=[history_table, avg_quality, avg_ratio, selected_id, diff_panel, delete_status],
|
| 94 |
)
|
| 95 |
|
| 96 |
return tab
|
|
|
|
| 11 |
"feedback", "feedback_comment",
|
| 12 |
]
|
| 13 |
|
| 14 |
+
_SESSION_WARNING = (
|
| 15 |
+
'<div style="background:#fef9c3;border:1px solid #eab308;color:#854d0e;'
|
| 16 |
+
'padding:8px 12px;border-radius:6px;font-size:0.9rem;margin-bottom:4px">'
|
| 17 |
+
'β οΈ <strong>Session only</strong> β history is stored in memory and will be '
|
| 18 |
+
'cleared when you close or refresh this page. No data is persisted to disk.'
|
| 19 |
+
'</div>'
|
| 20 |
+
)
|
| 21 |
|
| 22 |
+
|
| 23 |
+
def load_history(selected_cols, run_store):
|
| 24 |
cols = selected_cols if selected_cols else _DEFAULT_COLS
|
| 25 |
+
runs = get_runs(run_store, limit=100)
|
| 26 |
if not runs:
|
| 27 |
return pd.DataFrame(columns=cols), "", "", ""
|
| 28 |
df = pd.DataFrame(runs)
|
|
|
|
| 33 |
return df, avg_quality, avg_ratio, ""
|
| 34 |
|
| 35 |
|
| 36 |
+
def on_row_select(evt: gr.SelectData, df: pd.DataFrame, run_store: list):
|
| 37 |
if df is None or df.empty:
|
| 38 |
return None, "", "No rows available."
|
| 39 |
row_idx = evt.index[0]
|
| 40 |
run_id = int(df.iloc[row_idx]["id"])
|
| 41 |
+
record = get_run(run_store, run_id)
|
| 42 |
if not record:
|
| 43 |
+
return None, "", f"Row {run_id} not found."
|
| 44 |
diff_html = render_diff_html(record)
|
| 45 |
return run_id, diff_html, f"Row {run_id} selected β click Delete to remove."
|
| 46 |
|
| 47 |
|
| 48 |
+
def delete_selected(run_id, selected_cols, run_store):
|
| 49 |
if run_id is None:
|
| 50 |
+
df, avg_q, avg_r, _ = load_history(selected_cols, run_store)
|
| 51 |
+
return df, avg_q, avg_r, None, "", "No row selected.", run_store
|
| 52 |
+
new_store = delete_run(run_store, run_id)
|
| 53 |
+
df, avg_q, avg_r, _ = load_history(selected_cols, new_store)
|
| 54 |
+
return df, avg_q, avg_r, None, "", f"Row {run_id} deleted.", new_store
|
| 55 |
|
| 56 |
|
| 57 |
+
def build_history_tab(run_store) -> gr.Tab:
|
| 58 |
with gr.Tab("History") as tab:
|
| 59 |
gr.Markdown("## Compression Run History")
|
| 60 |
+
gr.HTML(_SESSION_WARNING)
|
| 61 |
|
| 62 |
with gr.Row():
|
| 63 |
refresh_btn = gr.Button("Refresh", variant="secondary")
|
|
|
|
| 88 |
|
| 89 |
_outputs = [history_table, avg_quality, avg_ratio, diff_panel]
|
| 90 |
|
| 91 |
+
refresh_btn.click(fn=load_history, inputs=[col_picker, run_store], outputs=_outputs)
|
| 92 |
+
tab.select(fn=load_history, inputs=[col_picker, run_store], outputs=_outputs)
|
| 93 |
+
col_picker.change(fn=load_history, inputs=[col_picker, run_store], outputs=_outputs)
|
| 94 |
history_table.select(
|
| 95 |
fn=on_row_select,
|
| 96 |
+
inputs=[history_table, run_store],
|
| 97 |
outputs=[selected_id, diff_panel, delete_status],
|
| 98 |
)
|
| 99 |
delete_btn.click(
|
| 100 |
fn=delete_selected,
|
| 101 |
+
inputs=[selected_id, col_picker, run_store],
|
| 102 |
+
outputs=[history_table, avg_quality, avg_ratio, selected_id, diff_panel, delete_status, run_store],
|
| 103 |
)
|
| 104 |
|
| 105 |
return tab
|