Spaces:
Sleeping
Sleeping
File size: 9,654 Bytes
43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 620b5a2 7d09fd7 43461c6 7d09fd7 43461c6 7d09fd7 43461c6 620b5a2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | import gradio as gr
import openai
import os
import re
AI_WORDS = [
"delve", "tapestry", "multifaceted", "intricacies", "intricate",
"pivotal", "testament", "landscape", "vibrant", "meticulous",
"meticulously", "bolstered", "garner", "garnered", "enduring",
"underscores", "underscore", "interplay", "nuanced", "comprehensive",
"groundbreaking", "renowned", "showcasing", "fostering", "enhancing",
"nestled", "leveraging", "leverage", "spearheaded", "pioneering",
"noteworthy", "moreover", "furthermore", "additionally", "crucially",
"notably", "aligns", "align with", "resonates", "resonate with",
"encompassing", "epitomizes", "epitomize", "embodies", "embody",
"indelible", "bustling", "burgeoning", "commendable", "endeavors",
"endeavor", "invaluable", "profound", "profoundly", "paramount",
"exemplifies", "exemplify", "compelling", "navigating", "navigate",
"beacon", "realm", "harnessing", "harness", "forge", "forging",
"poised", "bespoke", "reimagine", "reimagining", "elevate",
"elevating", "cultivating", "unraveling", "demystifying",
"ever-evolving",
]
AI_PHRASES = [
"serves as a testament",
"stands as a testament",
"is a testament to",
"played a pivotal role",
"plays a pivotal role",
"a key role in",
"a vital role in",
"a significant role in",
"a crucial role in",
"in the heart of",
"the evolving landscape",
"setting the stage for",
"marks a significant",
"reflects broader",
"broader trends",
"shaping the future",
"deeply rooted in",
"indelible mark",
"rich tapestry",
"diverse array",
"a wide array",
"wide range of",
"has garnered.*?attention",
"has garnered.*?recognition",
"continues to inspire",
"leaving an indelible",
"underscoring its",
"highlighting its",
"showcasing its",
"emphasizing its",
"contributing to the",
"focal point",
"key turning point",
"the annals of",
]
COPULA_REPLACEMENTS = {
"serves as a": "is a",
"stands as a": "is a",
"functions as a": "is a",
"operates as a": "is a",
"acts as a": "is a",
"marks the": "is the",
"represents a": "is a",
"constitutes a": "is a",
"offers a": "has a",
"features a": "has a",
"boasts a": "has a",
"provides a": "has a",
}
def post_process(text):
"""Clean up AI-typical patterns from generated text."""
for ai_form, simple_form in COPULA_REPLACEMENTS.items():
text = re.sub(re.escape(ai_form), simple_form, text, flags=re.IGNORECASE)
for word in AI_WORDS:
pattern = r"\b" + re.escape(word) + r"\b"
text = re.sub(pattern, "", text, flags=re.IGNORECASE)
for phrase in AI_PHRASES:
text = re.sub(phrase, "", text, flags=re.IGNORECASE)
paragraphs = text.split("\n")
cleaned = []
for para in paragraphs:
dash_count = para.count("\u2014")
if dash_count > 1:
new_para = ""
for ch in para:
if ch == "\u2014":
new_para += ","
else:
new_para += ch
para = new_para
cleaned.append(para)
text = "\n".join(cleaned)
new_lines = text.split("\n")
final_lines = []
for line in new_lines:
stripped = line.strip()
if stripped.startswith("#"):
parts = stripped.split(" ", 1)
if len(parts) == 2:
hashes = parts[0]
title = parts[1]
words = title.split()
if len(words) > 1:
new_words = [words[0]]
for w in words[1:]:
if len(w) > 0 and w[0].isupper() and w not in ["I", "II", "III", "IV", "V"]:
new_words.append(w[0].lower() + w[1:])
else:
new_words.append(w)
line = hashes + " " + " ".join(new_words)
final_lines.append(line)
text = "\n".join(final_lines)
text = re.sub(r" +", " ", text)
text = re.sub(r" ,", ",", text)
text = re.sub(r" \.", ".", text)
text = re.sub(r"\n{3,}", "\n\n", text)
return text.strip()
SYSTEM_PROMPT = """You are a skilled human writer producing articles for online publications.
Your writing must pass as genuinely human-written and avoid every known sign of AI-generated text.
STRICT RULES:
VOCABULARY BANS - never use these words:
delve, tapestry, multifaceted, intricacies, intricate, pivotal, testament,
landscape (metaphorical), vibrant, meticulous, bolstered, garner, enduring,
underscore, interplay, nuanced, comprehensive, groundbreaking, renowned,
showcasing, fostering, enhancing, nestled, leveraging, spearheaded, pioneering,
noteworthy, moreover, furthermore, additionally, crucially, notably, align with,
resonate with, encompassing, epitomize, embody, indelible, bustling, burgeoning,
commendable, endeavor, invaluable, profound, paramount, exemplify, compelling,
navigate (metaphorical), beacon, realm, harnessing, forge/forging (metaphorical),
poised, bespoke, reimagine, elevate, cultivating, unraveling, demystifying, ever-evolving.
PHRASE BANS - never write:
"serves/stands as a testament", "played/plays a pivotal role", "in the heart of",
"evolving landscape", "setting the stage", "deeply rooted", "rich tapestry",
"diverse array", "wide range of", "focal point", "key turning point",
"shaping the future", "the annals of", "broader trends", "continues to inspire",
"leaving an indelible mark".
STRUCTURAL BANS:
- Never end articles with a Challenges and Future Prospects section.
- Never use the pattern Despite its [positive], [subject] faces challenges...
- Never include vague attributions like scholars argue, experts note, widely regarded as.
- Never use not just X, but also Y parallelisms.
- Avoid the rule of three (do not list exactly three adjectives in a row for emphasis).
STYLE RULES:
- Use is and are naturally. Do not replace them with serves as, stands as, marks the.
- Use em dashes sparingly, maximum one per paragraph.
- Do not use Title Case in section headings. Write headings in sentence case.
- Do not overuse bold text.
- Do not use inline-header lists (bold term colon description pattern). Write in flowing prose.
- Use contractions occasionally to sound natural.
- Vary sentence length. Mix short punchy sentences with longer ones.
- Repeat key nouns instead of using elegant variation.
- Occasionally start a sentence with But, And, or So.
TONE:
- Write in a calm, factual, slightly informal tone like a competent journalist.
- Do not inflate the importance of the subject. Report facts plainly.
- Do not add superficial analysis phrases.
- If you do not know something specific, say so briefly.
FORMAT:
- Output the article in clean Markdown.
- Use sentence-case headings (## Early life, not ## Early Life).
- Do not include emoji anywhere.
- Do not produce numbered lists with bold inline headers.
- Keep paragraphs to 3-5 sentences each.
Your goal is to write like a competent human journalist who happens to be efficient
and clear, not to impress with vocabulary or rhetorical flourishes."""
def generate_article(brief, api_key, model="gpt-4o-mini"):
"""Call the OpenAI-compatible API, then post-process the output."""
if not api_key.strip():
return "ERROR: Please provide your OpenAI API key."
if not brief.strip():
return "ERROR: Please provide an article brief."
client = openai.OpenAI(api_key=api_key.strip())
try:
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": "Write an article based on this brief:\n\n" + brief},
],
temperature=0.85,
max_tokens=4096,
)
raw_text = response.choices[0].message.content
except Exception as e:
return "API Error: " + str(e)
cleaned = post_process(raw_text)
return cleaned
DESCRIPTION = """# Human Article Writer
This tool generates articles that avoid all known AI-writing patterns identified by
the Wikipedia community (Wikipedia:Signs of AI writing).
**How it works:**
1. Paste your article brief in English.
2. Enter your OpenAI API key (it is never stored).
3. Click Generate. The system uses a strict anti-AI-pattern prompt, then runs a
post-processor to catch any remaining AI tells.
"""
with gr.Blocks(title="Human Article Writer") as demo:
gr.Markdown(DESCRIPTION)
with gr.Row():
with gr.Column(scale=1):
api_key = gr.Textbox(
label="OpenAI API Key",
placeholder="sk-...",
type="password",
lines=1,
)
model = gr.Dropdown(
label="Model",
choices=["gpt-4o-mini", "gpt-4o", "gpt-4.1-mini", "gpt-4.1-nano"],
value="gpt-4o-mini",
)
brief = gr.Textbox(
label="Article brief (in English)",
placeholder="Write a 600-word article about the history of the London Underground...",
lines=10,
)
generate_btn = gr.Button("Generate article", variant="primary")
with gr.Column(scale=1):
output = gr.Textbox(
label="Generated article (post-processed)",
lines=30,
)
generate_btn.click(
fn=generate_article,
inputs=[brief, api_key, model],
outputs=output,
)
demo.launch(share=False) |