'
'Activate an agent and check Add to Collection'
' to be the first.
'
)
cards = ""
for i, agent in enumerate(reversed(agents)):
name = agent.get("name", "Unnamed Agent")
prompt = agent.get("system_prompt", "")
date = agent.get("timestamp", "")[:10]
color = _agent_color(i)
chars = len(prompt)
preview = _prompt_preview(prompt, 80)
n_js = _js_escape(name)
p_js = _js_escape(prompt)
safe_preview = preview.replace("<", "<").replace(">", ">")
cards += (
f'
'
f'
'
f'{name}'
f'{date}'
f'
'
# Expandable preview using
f''
f'{safe_preview}'
f'
'
f'{prompt[:800].replace("<","<").replace(">",">")}{"…" if len(prompt)>800 else ""}'
f'
'
f'
'
f''
f'Load into Forge →'
f'{chars:,} chars'
f'
'
f'
'
)
return (
f'
'
f'
'
f'⚡ {count} Agent{"s" if count!=1 else ""} Forged'
f'
'
f'{bucket_badge}{refresh_btn}'
f'
'
f'{cards}'
f'
'
)
def _render_quick_list() -> str:
agents = _collection_agents()
if not agents:
return '
No agents yet — forge the first one! ⚡
'
items = ""
for i, agent in enumerate(reversed(agents[:10])):
name = agent.get("name", "Unnamed")
prompt = agent.get("system_prompt", "")
color = _agent_color(i)
n_js = _js_escape(name)
p_js = _js_escape(prompt)
items += (
f'
'
f''
f'{name}'
f'Load →'
f'
'
)
if len(agents) > 10:
items += f'
+ {len(agents)-10} more
'
return items
def _render_active_badge(name: str) -> str:
if not name or name == "No agent active":
return '
No agent active
'
safe = name[:40] + ("…" if len(name) > 40 else "")
return f'
Drop .md · .skill · .txt · any text file → fill system prompt → chat → share to the Collection
""")
with gr.Row(equal_height=False):
# ── LEFT ──────────────────────────────────────────────────────────────
with gr.Column(scale=4, min_width=280):
gr.HTML('
📁 Drop Files
')
file_upload = gr.File(
label="",
file_count="multiple",
file_types=[".md", ".txt", ".skill", ".yaml", ".yml",
".json", ".py", ".ts", ".js", ".toml"],
height=110,
)
prompt_box = gr.Textbox(
label="System Prompt",
placeholder="Paste or type your system prompt…\nOr drop files above ↑\n\nTip: multiple files are auto-concatenated.",
lines=9,
max_lines=22,
)
agent_name_box = gr.Textbox(
label="Agent Name",
placeholder="e.g. Code Reviewer · Sales Coach · SQL Expert",
max_lines=1,
)
in_coll_check = gr.Checkbox(
label="🌐 Add to public Agent Collection",
value=False,
info="Saves the raw system prompt string to the shared HF Bucket.",
)
activate_btn = gr.Button("⚡ Activate Agent", variant="primary", size="lg")
status_out = gr.HTML("")
with gr.Accordion("⚙️ Model Settings", open=False):
max_tokens_slider = gr.Slider(
minimum=256, maximum=8192, value=1024, step=128,
label="Max output tokens",
)
gr.Markdown(f"*Model: `{MODEL_ID}`*")
gr.HTML("")
with gr.Accordion("📚 Quick Load from Collection", open=True):
quick_list_html = gr.HTML(_render_quick_list())
# Signal‑Textbox (unsichtbar, aber aktiv)
agent_load_signal = gr.Textbox(
value="",
elem_id="agent_load_signal",
label="",
visible=True,
)
# ── RIGHT ─────────────────────────────────────────────────────────────
with gr.Column(scale=6):
active_badge_html = gr.HTML(_render_active_badge("No agent active"))
chatbot = gr.Chatbot(
height=440,
show_label=False,
)
# Chat input row
with gr.Row():
msg_box = gr.Textbox(
placeholder="Message your agent…",
show_label=False,
scale=7,
max_lines=4,
)
send_btn = gr.Button("Send", variant="primary", scale=2, min_width=80)
clear_btn = gr.Button("Clear", variant="secondary", scale=1, min_width=70)
# ── Tabs ──────────────────────────────────────────────────────────────────
gr.HTML("")
with gr.Tabs():
with gr.Tab("🌐 Agent Collection"):
collection_out = gr.HTML(_render_collection())
hidden_refresh_btn = gr.Button("refresh", elem_id="hidden_refresh_btn", visible=False)
with gr.Tab("ℹ️ About"):
gr.Markdown("""
## PromptHub ⚡
**Forge any AI agent from plain text files.**
Drop `.md`, `.skill`, `.txt`, `.yaml`, or any text file — PromptHub reads them,
concatenates their content, and uses it as the system prompt for a live chat session.
### How it works
1. **Drop files** or paste text directly into the *System Prompt* box
2. Give your agent a **name**
3. Optionally **add it to the Collection** so others can load and use it
4. Hit **⚡ Activate Agent** — then chat!
### Storage
All agents are stored in `/data/agents.jsonl` via HF Bucket (auto-mounted).
```bash
# Download the bucket
hf sync hf://buckets/aidn/AnyAgent-storage ./local
```
### Secrets
| Secret | Purpose |
|--------|---------|
| `HF_TOKEN` | Required for inference |
| `MODEL_ID` | Optional model override |
""")
if not HF_TOKEN:
gr.HTML(_status_html(
"No HF_TOKEN — add it in Settings → Secrets.", "warn"))
# ── Events ────────────────────────────────────────────────────────────────
file_upload.change(
fn=files_to_prompt,
inputs=[file_upload, prompt_box],
outputs=[prompt_box],
)
activate_btn.click(
fn=activate_agent,
inputs=[agent_name_box, prompt_box, in_coll_check],
outputs=[system_prompt_state, status_out,
collection_out, quick_list_html, active_name_state],
).then(
fn=_render_active_badge,
inputs=[active_name_state],
outputs=[active_badge_html],
)
def _submit(msg, hist):
if not msg.strip():
return gr.update(), hist
return "", hist + [
{"role": "user", "content": msg},
{"role": "assistant", "content": ""},
]
msg_box.submit(
fn=_submit,
inputs=[msg_box, chatbot],
outputs=[msg_box, chatbot],
).then(
fn=_bot_stream,
inputs=[chatbot, system_prompt_state, max_tokens_slider],
outputs=[chatbot],
)
send_btn.click(
fn=_submit,
inputs=[msg_box, chatbot],
outputs=[msg_box, chatbot],
).then(
fn=_bot_stream,
inputs=[chatbot, system_prompt_state, max_tokens_slider],
outputs=[chatbot],
)
clear_btn.click(fn=lambda: [], outputs=[chatbot])
agent_load_signal.change(
fn=_load_signal,
inputs=[agent_load_signal],
outputs=[agent_name_box, prompt_box, system_prompt_state, active_name_state],
).then(
fn=_render_active_badge,
inputs=[active_name_state],
outputs=[active_badge_html],
)
hidden_refresh_btn.click(
fn=lambda: (_render_collection(), _render_quick_list()),
outputs=[collection_out, quick_list_html],
)
if __name__ == "__main__":
demo.launch(
css=CSS,
head=JS_HEAD,
)