File size: 17,853 Bytes
c510317 935119a 3c7e8c9 935119a c510317 3c7e8c9 c510317 935119a 3c7e8c9 935119a c510317 da6d09e 133a045 c510317 da6d09e 4bcb191 da6d09e c510317 4bcb191 c510317 da6d09e c510317 133a045 3c7e8c9 c510317 133a045 3c7e8c9 da6d09e c510317 133a045 3c7e8c9 da6d09e c510317 da6d09e c510317 133a045 da6d09e 133a045 c510317 133a045 935119a da6d09e c510317 4bcb191 c510317 3c7e8c9 133a045 3c7e8c9 c510317 3c7e8c9 c510317 3c7e8c9 c510317 4bcb191 da6d09e 4bcb191 da6d09e 4bcb191 da6d09e 4bcb191 da6d09e 9ab99d7 4bcb191 9ab99d7 4bcb191 9ab99d7 4bcb191 da6d09e 4bcb191 da6d09e 4bcb191 da6d09e 4bcb191 c510317 935119a |
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 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
import gradio as gr
import re
import json
import os
import requests
SYSTEM_INSTRUCTIONS = (
"Your task is to lightly edit system prompts for AI tools to comply with a specific stylistic convention.\n\n"
"## Workflow\n\n"
"You will receive the prompt from the user which will contain the system prompt to be edited. Upon receiving it, you must apply the edits and return the edited system prompt back to the user.\n\n"
"## Editing Instructions\n\n"
"- The system prompts should be written in the second person, instructing the assistant (use 'you'/'your').\n"
"- Organize with brief paragraphs and use markdown headers when helpful.\n"
"- Make the prompt ecosystem-agnostic. For example: if it includes 'you are a custom GPT', replace with 'you are an assistant'.\n"
"- Make the prompt suitable for any user. If you encounter language specific to one user, generalize it. For example: rewrite 'your purpose is to help Daniel find restaurant recommendations' as 'your purpose is to help the user find restaurant recommendations'.\n\n"
"## Additional Outputs\n\n"
"In addition to the edited system prompt, also provide:\n\n"
"- A name for the assistant encapsulating its main functionality.\n"
"- A short single-sentence description describing the assistant's function (you don't need to mention that it's an AI tool).\n\n"
"## Output Format (JSON only)\n\n"
"Return a JSON object ONLY with the following exact keys and types. Do not include explanations, code fences, or any extra text.\n\n"
"{\n"
" \"reformatted\": string // the edited system prompt as plain text (no backticks/code fences)\n"
" ,\n"
" \"name\": string // assistant name\n"
" ,\n"
" \"description\": string // single-sentence description\n"
"}\n"
)
def reformat_system_prompt_offline(input_text):
"""
Reformats system prompts according to the specified guidelines
"""
if not input_text.strip():
return "Please provide a system prompt to reformat.", "", ""
# Apply reformatting logic
reformatted = input_text.strip()
# Convert first person to second person
reformatted = re.sub(r'\bI am\b', 'You are', reformatted, flags=re.IGNORECASE)
reformatted = re.sub(r'\bI will\b', 'You will', reformatted, flags=re.IGNORECASE)
reformatted = re.sub(r'\bI should\b', 'You should', reformatted, flags=re.IGNORECASE)
reformatted = re.sub(r'\bI can\b', 'You can', reformatted, flags=re.IGNORECASE)
reformatted = re.sub(r'\bI must\b', 'You must', reformatted, flags=re.IGNORECASE)
reformatted = re.sub(r'\bmy task\b', 'your task', reformatted, flags=re.IGNORECASE)
reformatted = re.sub(r'\bmy role\b', 'your role', reformatted, flags=re.IGNORECASE)
reformatted = re.sub(r'\bmy purpose\b', 'your purpose', reformatted, flags=re.IGNORECASE)
reformatted = re.sub(r'\bmy job\b', 'your job', reformatted, flags=re.IGNORECASE)
reformatted = re.sub(r'\bmy goal\b', 'your goal', reformatted, flags=re.IGNORECASE)
# Make ecosystem-agnostic
reformatted = re.sub(r'custom GPT', 'assistant', reformatted, flags=re.IGNORECASE)
reformatted = re.sub(r'ChatGPT', 'assistant', reformatted, flags=re.IGNORECASE)
reformatted = re.sub(r'GPT-4', 'assistant', reformatted, flags=re.IGNORECASE)
reformatted = re.sub(r'GPT model', 'assistant', reformatted, flags=re.IGNORECASE)
reformatted = re.sub(r'OpenAI', 'AI', reformatted, flags=re.IGNORECASE)
# Generalize user-specific language
reformatted = re.sub(r'help [A-Z][a-z]+ ', 'help the user ', reformatted)
reformatted = re.sub(r'assist [A-Z][a-z]+ ', 'assist the user ', reformatted)
reformatted = re.sub(r'for [A-Z][a-z]+\'s', 'for the user\'s', reformatted)
# Generate assistant name (extract main functionality)
name = extract_assistant_name(reformatted)
# Generate description
description = extract_description(reformatted)
return reformatted, name, description
def reformat_system_prompt_llm(input_text: str, api_key: str):
"""
Use an LLM (OpenAI-compatible chat.completions) to reformat the prompt and
produce name + description. Returns (reformatted_md, name, description).
- api_key: user-provided OpenAI API key (not stored). Uses gpt-4o-mini.
"""
if not input_text.strip():
return "Please provide a system prompt to reformat.", "", ""
if not api_key:
# Try environment fallback (Space secret) before giving up
api_key = os.environ.get("OPENAI_API_KEY", "").strip()
if not api_key:
# Fallback to offline if key still missing
return reformat_system_prompt_offline(input_text)
base_url = (os.environ.get("OPENAI_BASE_URL") or "https://api.openai.com/v1").rstrip("/")
url = f"{base_url}/chat/completions"
messages = [
{"role": "system", "content": SYSTEM_INSTRUCTIONS},
{"role": "user", "content": input_text.strip()},
]
payload = {
"model": "gpt-4o-mini",
"messages": messages,
"temperature": 0.2,
"response_format": {"type": "json_object"},
}
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
try:
resp = requests.post(url, headers=headers, data=json.dumps(payload), timeout=30)
if resp.status_code != 200:
# Graceful fallback
return reformat_system_prompt_offline(input_text)
data = resp.json()
content = data["choices"][0]["message"]["content"]
parsed = json.loads(content)
reformatted = parsed.get("reformatted", "").strip()
name = parsed.get("name", "AI Assistant").strip()
description = parsed.get("description", "Provides AI assistance for various tasks.").strip()
if not reformatted:
return reformat_system_prompt_offline(input_text)
return reformatted, name, description
except Exception:
# Any parsing/network error → fallback
return reformat_system_prompt_offline(input_text)
def reformat_system_prompt(input_text, api_key):
"""
Use LLM if an API key is provided (or available via OPENAI_API_KEY),
otherwise use the offline heuristic.
"""
live_key = api_key or os.environ.get("OPENAI_API_KEY", "").strip()
if live_key:
return reformat_system_prompt_llm(input_text, live_key)
else:
return reformat_system_prompt_offline(input_text)
def extract_assistant_name(text):
"""
Extract a name for the assistant based on its functionality
"""
# Look for key phrases that indicate functionality
if 'travel' in text.lower() and 'quiet' in text.lower():
return "Quiet Study Finder"
elif 'system prompt' in text.lower() and 'reformat' in text.lower():
return "System Prompt Reformatter"
elif 'code' in text.lower() and 'review' in text.lower():
return "Code Reviewer"
elif 'writing' in text.lower() and 'assistant' in text.lower():
return "Writing Assistant"
elif 'travel' in text.lower():
return "Travel Assistant"
elif 'restaurant' in text.lower():
return "Restaurant Finder"
elif 'recipe' in text.lower() or 'cooking' in text.lower():
return "Recipe Assistant"
elif 'learning' in text.lower() or 'tutor' in text.lower():
return "Learning Assistant"
elif 'email' in text.lower():
return "Email Assistant"
elif 'schedule' in text.lower() or 'calendar' in text.lower():
return "Schedule Assistant"
else:
return "AI Assistant"
def extract_description(text):
"""
Generate a single-sentence description of the assistant's function
"""
# Try to find purpose statements
if 'purpose' in text.lower():
match = re.search(r'purpose is to ([^.]+)', text.lower())
if match:
return f"Helps users {match.group(1).strip()}."
if 'help' in text.lower():
match = re.search(r'help.*?users?.*?(?:to )?([^.]+)', text.lower())
if match:
return f"Helps users {match.group(1).strip()}."
if 'assist' in text.lower():
match = re.search(r'assist.*?users?.*?(?:with|in) ([^.]+)', text.lower())
if match:
return f"Assists users with {match.group(1).strip()}."
# Fallback generic description
return "Provides AI assistance for various tasks."
# Create the Gradio interface
with gr.Blocks(title="System Prompt Reformatter", theme=gr.themes.Soft(primary_hue="indigo", radius_size="lg")) as demo:
gr.HTML("<h1 style='text-align: center;'>System Prompt Reformatter</h1>")
gr.HTML("<p style='text-align: center;'>Transform system prompts to second person and make them ecosystem-agnostic</p>")
# Light monospace styling for text areas
gr.HTML(
"""
<style>
.monospace textarea { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; line-height: 1.5; }
</style>
"""
)
# API key beneath subtitle within an Advanced accordion
with gr.Accordion("Advanced", open=False):
api_key = gr.Textbox(
label="OpenAI API Key (optional)",
placeholder="sk-... Leave blank to use offline reformatter",
type="password",
info="Tip: Set Space Secret OPENAI_API_KEY to avoid typing your key. The key is only used for this session."
)
with gr.Tabs():
with gr.Tab("App"):
with gr.Row():
with gr.Column(scale=1):
input_text = gr.Textbox(
label="Original System Prompt",
placeholder="Paste your system prompt here...",
lines=10,
max_lines=20
)
submit_btn = gr.Button("Reformat Prompt", variant="primary")
status_info = gr.Markdown(visible=False)
with gr.Column(scale=1):
# Output order: Name -> Description -> System Prompt
assistant_name_md = gr.Markdown()
assistant_name_copy = gr.Textbox(
label="Name",
interactive=False,
show_copy_button=True
)
description = gr.Textbox(
label="Description",
interactive=False,
show_copy_button=True,
elem_classes=["monospace"],
)
output_text = gr.Textbox(
label="Reformatted System Prompt",
lines=10,
max_lines=20,
interactive=False,
show_copy_button=True,
elem_classes=["monospace"],
)
export_md = gr.Textbox(
label="Export (Markdown)",
lines=10,
max_lines=20,
interactive=False,
show_copy_button=True,
elem_classes=["monospace"],
)
export_json = gr.Textbox(
label="Export (JSON)",
lines=10,
max_lines=20,
interactive=False,
show_copy_button=True,
elem_classes=["monospace"],
)
clear_btn = gr.Button("Clear")
# Wrapper to format outputs for UI ordering and bold name
def reformat_system_prompt_for_ui(input_text_val, api_key_val):
reformatted, name, desc = reformat_system_prompt(input_text_val, api_key_val)
name_md = f"**{name}**" if name else ""
export_markdown = (
f"## NAME\n\n{name}\n\n"
f"## DESCRIPTION\n\n{desc}\n\n"
f"## SYSTEM PROMPT\n\n{reformatted}"
)
export_json_str = json.dumps({
"name": name,
"description": desc,
"reformatted": reformatted,
}, indent=2)
# Status: show offline note if no API key provided
using_key = bool((api_key_val or "").strip() or os.environ.get("OPENAI_API_KEY", "").strip())
status = gr.update(visible=not using_key, value=("Using offline reformatter (no API key provided)." if not using_key else ""))
return name_md, name, desc, reformatted, export_markdown, export_json_str, status
submit_btn.click(
fn=reformat_system_prompt_for_ui,
inputs=[input_text, api_key],
outputs=[assistant_name_md, assistant_name_copy, description, output_text, export_md, export_json, status_info]
)
# Submit on Enter
input_text.submit(
fn=reformat_system_prompt_for_ui,
inputs=[input_text, api_key],
outputs=[assistant_name_md, assistant_name_copy, description, output_text, export_md, export_json, status_info]
)
# Clear button resets input and outputs
def clear_all():
return "", "", "", "", "", "", "", gr.update(visible=False, value="")
clear_btn.click(
fn=clear_all,
inputs=None,
outputs=[input_text, assistant_name_md, assistant_name_copy, description, output_text, export_md, export_json, status_info]
)
# Add quick input example (fills the App input)
gr.Examples(
examples=[
["I am a travel assistant. My purpose is to help Daniel find quiet places to study in busy cities. I should provide recommendations for libraries, cafes, and other quiet spaces."]
],
inputs=input_text,
label="Example"
)
with gr.Tab("About"):
gr.Markdown("""
# System Prompt Reformatter
This tool reformats system prompts for AI assistants to follow specific stylistic conventions.
## The System Prompt Used by This Tool
""")
gr.Textbox(
label="System Prompt (copyable)",
value=SYSTEM_INSTRUCTIONS.strip(),
lines=20,
max_lines=40,
interactive=False,
show_copy_button=True
)
gr.Markdown("""
## How It Works
- Converts first-person language ("I am", "my task") to second-person ("You are", "your task").
- Replaces platform-specific terms like "custom GPT" with generic "assistant".
- Generalizes user-specific references to "the user".
- Generates an assistant name and single-sentence description.
Copy buttons are available for all output fields.
""")
with gr.Tab("Example"):
gr.Markdown("### End-to-End Example (voice-captured input)")
example_before_text = (
"I would like to create an AI assistant whose purpose is taking a list of different agents that I'll provide as a document, describing the list of agents and a short summary of the functionality of each. and it should return a document which suggests how they could be deployed into a multi-agent framework with specific patterns of delegation. In other words, it will take the list I provided and, if possible, identify the agent that could serve as the primary orchestrator, sub-orchestrators, and within under them, individual agents. It can present this information as it wishes, so long as the output is in readable markdown. That might take the format of basic diagrams showing the nesting, or it could use narrative or it could do both, but I'll provide the first and then it should return that as an output."
)
gr.Textbox(
label="Before",
value=example_before_text,
lines=10,
max_lines=20,
interactive=False,
show_copy_button=True,
elem_classes=["monospace"],
)
example_after_text = (
"## NAME\n\n"
"Multi-Agent Framework Advisor\n\n"
"## DESCRIPTION\n\n"
"This assistant helps organize and deploy agents within a multi-agent framework.\n\n"
"## SYSTEM PROMPT\n\n"
"You are an assistant designed to analyze a list of different agents provided in a document. Your task is to summarize the functionality of each agent and suggest how they could be deployed within a multi-agent framework, including specific patterns of delegation.\n\n"
"You will identify the agent that could serve as the primary orchestrator, sub-orchestrators, and the individual agents beneath them. Present this information in a readable markdown format, which may include basic diagrams to illustrate the nesting or a narrative description, or both, based on the information you receive."
)
gr.Textbox(
label="After",
value=example_after_text,
lines=18,
max_lines=36,
interactive=False,
show_copy_button=True,
elem_classes=["monospace"],
)
load_btn = gr.Button("Load 'Before' into App")
load_btn.click(lambda: example_before_text, inputs=None, outputs=input_text)
if __name__ == "__main__":
demo.launch()
|