feat: frame output for copy-to-LLM use with one-click copy button
Browse filesClarify the core flow: extract locally, copy the redacted output, paste into
any LLM with no raw PII exposed.
- app.py: add a one-click copy button on the safe output (version-safe via
_COPY, since show_copy_button exists in Gradio 5.x but not 6.x); relabel
outputs ("Safe output β copy into your LLM" / "Extracted fields β stays
local, never sent"); reword the header to describe the copy-to-LLM flow.
Co-authored-by: OpenAI Codex <codex@openai.com>
app.py
CHANGED
|
@@ -21,6 +21,16 @@ from redac import (
|
|
| 21 |
)
|
| 22 |
from redac.detect import gliner_available
|
| 23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
_NER_NOTE = (
|
| 25 |
"" if gliner_available()
|
| 26 |
else "\n\n> β οΈ GLiNER not installed: running **regex-only** (structured "
|
|
@@ -64,8 +74,10 @@ def do_rehydrate(redacted_text, mapping):
|
|
| 64 |
with gr.Blocks(title="Redac", theme=gr.themes.Soft()) as demo:
|
| 65 |
gr.Markdown(
|
| 66 |
"# ποΈ Redac\n"
|
| 67 |
-
"**A local privacy gateway.**
|
| 68 |
-
"
|
|
|
|
|
|
|
| 69 |
)
|
| 70 |
|
| 71 |
with gr.Tabs():
|
|
@@ -87,7 +99,11 @@ with gr.Blocks(title="Redac", theme=gr.themes.Soft()) as demo:
|
|
| 87 |
t_thr = gr.Slider(0.1, 0.9, value=0.45, step=0.05, label="Threshold")
|
| 88 |
t_btn = gr.Button("Redact", variant="primary")
|
| 89 |
with gr.Column():
|
| 90 |
-
t_out = gr.Textbox(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
t_sum = gr.Markdown()
|
| 92 |
t_tab = gr.Dataframe(
|
| 93 |
headers=["type", "value", "score", "source"],
|
|
@@ -117,8 +133,15 @@ with gr.Blocks(title="Redac", theme=gr.themes.Soft()) as demo:
|
|
| 117 |
i_thr = gr.Slider(0.1, 0.9, value=0.45, step=0.05, label="Threshold")
|
| 118 |
i_btn = gr.Button("Extract & redact", variant="primary")
|
| 119 |
with gr.Column():
|
| 120 |
-
i_extracted = gr.Textbox(
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
i_sum = gr.Markdown()
|
| 123 |
i_tab = gr.Dataframe(
|
| 124 |
headers=["type", "value", "score", "source"],
|
|
|
|
| 21 |
)
|
| 22 |
from redac.detect import gliner_available
|
| 23 |
|
| 24 |
+
import inspect
|
| 25 |
+
|
| 26 |
+
# show_copy_button exists in Gradio 5.x; in 6.x copying is the default and the
|
| 27 |
+
# kwarg was removed. Pass it only when supported so the app runs on both.
|
| 28 |
+
_COPY = (
|
| 29 |
+
{"show_copy_button": True}
|
| 30 |
+
if "show_copy_button" in inspect.signature(gr.Textbox.__init__).parameters
|
| 31 |
+
else {}
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
_NER_NOTE = (
|
| 35 |
"" if gliner_available()
|
| 36 |
else "\n\n> β οΈ GLiNER not installed: running **regex-only** (structured "
|
|
|
|
| 74 |
with gr.Blocks(title="Redac", theme=gr.themes.Soft()) as demo:
|
| 75 |
gr.Markdown(
|
| 76 |
"# ποΈ Redac\n"
|
| 77 |
+
"**A local privacy gateway.** Upload a document or paste text, Redac "
|
| 78 |
+
"extracts and redacts the PII *locally*, and you copy the safe output to "
|
| 79 |
+
"use with any LLM. Raw values never leave: the mapping stays local so you "
|
| 80 |
+
"can rehydrate answers yourself." + _NER_NOTE
|
| 81 |
)
|
| 82 |
|
| 83 |
with gr.Tabs():
|
|
|
|
| 99 |
t_thr = gr.Slider(0.1, 0.9, value=0.45, step=0.05, label="Threshold")
|
| 100 |
t_btn = gr.Button("Redact", variant="primary")
|
| 101 |
with gr.Column():
|
| 102 |
+
t_out = gr.Textbox(
|
| 103 |
+
label="β
Safe output β copy this into your LLM (no raw PII)",
|
| 104 |
+
lines=10,
|
| 105 |
+
**_COPY,
|
| 106 |
+
)
|
| 107 |
t_sum = gr.Markdown()
|
| 108 |
t_tab = gr.Dataframe(
|
| 109 |
headers=["type", "value", "score", "source"],
|
|
|
|
| 133 |
i_thr = gr.Slider(0.1, 0.9, value=0.45, step=0.05, label="Threshold")
|
| 134 |
i_btn = gr.Button("Extract & redact", variant="primary")
|
| 135 |
with gr.Column():
|
| 136 |
+
i_extracted = gr.Textbox(
|
| 137 |
+
label="π Extracted fields (raw β stays local, never sent)",
|
| 138 |
+
lines=8,
|
| 139 |
+
)
|
| 140 |
+
i_out = gr.Textbox(
|
| 141 |
+
label="β
Safe output β copy this into your LLM (no raw PII)",
|
| 142 |
+
lines=8,
|
| 143 |
+
**_COPY,
|
| 144 |
+
)
|
| 145 |
i_sum = gr.Markdown()
|
| 146 |
i_tab = gr.Dataframe(
|
| 147 |
headers=["type", "value", "score", "source"],
|