aria-v4 / app.py
ziggylott's picture
Deploy ARIA v4 customer onboarding app
9485856 verified
Raw
History Blame Contribute Delete
18.2 kB
"""
ARIA v4 — Customer API Key Setup + Onboarding UI
Built for Ting Lott's ARIA v4 product.
Each customer brings their own API keys — zero cost to Ting.
"""
import gradio as gr
import requests
import json
import os
from datetime import datetime
# ─────────────────────────────────────────────
# VALIDATION HELPERS
# ─────────────────────────────────────────────
def validate_groq_key(key: str) -> tuple[bool, str]:
if not key or len(key) < 20:
return False, "❌ Key too short — paste your full Groq key"
try:
r = requests.post(
"https://api.groq.com/openai/v1/chat/completions",
headers={"Authorization": f"Bearer {key.strip()}", "Content-Type": "application/json"},
json={"model": "llama-3.3-70b-versatile", "messages": [{"role": "user", "content": "Hi"}], "max_tokens": 5},
timeout=10
)
if r.status_code == 200:
return True, "✅ Groq key valid! Free AI connected."
elif r.status_code == 401:
return False, "❌ Invalid Groq key — copy it again from console.groq.com"
else:
return False, f"❌ Groq error {r.status_code} — try again"
except Exception as e:
return False, f"❌ Connection error: {str(e)[:60]}"
def validate_airtable_token(token: str, base_id: str) -> tuple[bool, str]:
if not token or not token.startswith("pat"):
return False, "❌ Airtable token must start with 'pat' — see instructions below"
if not base_id or not base_id.startswith("app"):
return False, "❌ Base ID must start with 'app' — see Step 2 below"
try:
r = requests.get(
f"https://api.airtable.com/v0/meta/bases/{base_id}/tables",
headers={"Authorization": f"Bearer {token.strip()}"},
timeout=10
)
if r.status_code == 200:
tables = r.json().get("tables", [])
return True, f"✅ Airtable connected! Found {len(tables)} table(s) in your base."
elif r.status_code == 401:
return False, "❌ Invalid token — create a new one at airtable.com/create/tokens"
elif r.status_code == 403:
return False, "❌ Token doesn't have access to this base — check scopes"
elif r.status_code == 404:
return False, "❌ Base ID not found — copy it from your Airtable base URL"
else:
return False, f"❌ Airtable error {r.status_code}"
except Exception as e:
return False, f"❌ Connection error: {str(e)[:60]}"
def validate_openai_key(key: str) -> tuple[bool, str]:
if not key:
return True, "⏭️ Skipped (optional)"
if not key.startswith("sk-"):
return False, "❌ OpenAI key must start with 'sk-'"
try:
r = requests.get(
"https://api.openai.com/v1/models",
headers={"Authorization": f"Bearer {key.strip()}"},
timeout=10
)
if r.status_code == 200:
return True, "✅ OpenAI connected — GPT-4 unlocked!"
else:
return False, f"❌ OpenAI error {r.status_code}"
except:
return False, "❌ Could not reach OpenAI"
# ─────────────────────────────────────────────
# GROQ CHAT (uses customer's key)
# ─────────────────────────────────────────────
def chat_with_aria(message: str, history: list, groq_key: str, airtable_token: str, base_id: str, setup_complete: bool) -> tuple:
if not setup_complete or not groq_key:
return history + [{"role": "assistant", "content": "⚠️ Please complete Setup first (Tab 1) before chatting with ARIA."}], gr.update()
system_prompt = """You are ARIA v4 — an Autonomous Responsive Intelligent Agent built for small business owners, coaches, and digital creators.
You help users:
- Generate content for social media (posts, captions, threads)
- Write email sequences and newsletters
- Create marketing copy and product descriptions
- Plan content calendars
- Automate repetitive tasks
- Build business systems
- Answer business strategy questions
You are warm, direct, and results-focused. You ask clarifying questions when needed. You give actionable, specific answers — not vague generic advice.
When generating content, always ask: what platform? what audience? what goal?"""
messages = [{"role": "system", "content": system_prompt}]
for h in history:
messages.append(h)
messages.append({"role": "user", "content": message})
try:
r = requests.post(
"https://api.groq.com/openai/v1/chat/completions",
headers={"Authorization": f"Bearer {groq_key.strip()}", "Content-Type": "application/json"},
json={"model": "llama-3.3-70b-versatile", "messages": messages, "max_tokens": 1000, "temperature": 0.7},
timeout=30
)
if r.status_code == 200:
reply = r.json()["choices"][0]["message"]["content"]
new_history = history + [
{"role": "user", "content": message},
{"role": "assistant", "content": reply}
]
return new_history, gr.update(value="")
else:
err = f"⚠️ API error {r.status_code}: {r.text[:100]}"
return history + [{"role": "assistant", "content": err}], gr.update()
except Exception as e:
return history + [{"role": "assistant", "content": f"⚠️ Error: {str(e)[:100]}"}], gr.update()
# ─────────────────────────────────────────────
# SETUP HANDLERS
# ─────────────────────────────────────────────
def save_groq_key(key):
ok, msg = validate_groq_key(key)
return msg, ok
def save_airtable(token, base_id):
ok, msg = validate_airtable_token(token, base_id)
return msg, ok
def save_openai(key):
ok, msg = validate_openai_key(key)
return msg, ok
def check_setup_complete(groq_valid, airtable_valid):
if groq_valid and airtable_valid:
return (
gr.update(visible=True),
"🚀 Setup complete! Switch to the **ARIA Chat** tab to start.",
True
)
missing = []
if not groq_valid:
missing.append("Groq API key")
if not airtable_valid:
missing.append("Airtable token + Base ID")
return (
gr.update(visible=False),
f"⏳ Still needed: {', '.join(missing)}",
False
)
# ─────────────────────────────────────────────
# GRADIO UI
# ─────────────────────────────────────────────
CSS = """
.setup-card { background: #1a1a2e; border: 1px solid #7c3aed; border-radius: 12px; padding: 20px; margin: 10px 0; }
.step-header { color: #a78bfa; font-size: 18px; font-weight: bold; margin-bottom: 8px; }
.instructions { color: #d1d5db; font-size: 13px; line-height: 1.6; }
.success-banner { background: #065f46; border: 1px solid #34d399; border-radius: 8px; padding: 12px; color: #a7f3d0; }
footer { display: none !important; }
"""
with gr.Blocks(theme=gr.themes.Soft(primary_hue="violet", neutral_hue="slate"), css=CSS, title="ARIA v4 Setup") as demo:
# ── STATE ──
groq_key_state = gr.State("")
airtable_token_state = gr.State("")
base_id_state = gr.State("")
openai_key_state = gr.State("")
groq_valid_state = gr.State(False)
airtable_valid_state = gr.State(False)
setup_complete_state = gr.State(False)
# ── HEADER ──
gr.Markdown("""
# 🤖 ARIA v4 — Autonomous Business AI
### Your personal AI that handles content, marketing & automation
**Welcome!** ARIA uses YOUR free API keys — you own your data, you control your usage.
Complete the 2-step setup below (takes about 5 minutes), then you're ready to go! 🚀
""")
with gr.Tabs():
# ════════════════════════════════
# TAB 1: SETUP
# ════════════════════════════════
with gr.Tab("🔧 Setup (Start Here)"):
gr.Markdown("## Step 1 of 2 — Get Your Free Groq API Key")
gr.Markdown("""
<div class="setup-card">
<div class="step-header">🧠 Groq — Your Free AI Brain (Required)</div>
<div class="instructions">
Groq gives you access to Llama 3.3 (one of the world's best AI models) — completely FREE.
**How to get your free key (2 minutes):**
1. Go to **[console.groq.com](https://console.groq.com)** and click **Sign Up** (use Google login)
2. After login, click **API Keys** in the left sidebar
3. Click **+ Create API Key** → name it anything (e.g. "ARIA v4")
4. Copy the key that starts with `gsk_...`
5. Paste it below and click **Connect Groq**
✅ Free tier includes: 14,400 requests/day, 500K tokens/minute — more than enough!
</div>
</div>
""")
groq_input = gr.Textbox(
placeholder="gsk_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
label="Groq API Key",
type="password",
show_label=True
)
groq_btn = gr.Button("🔌 Connect Groq", variant="primary")
groq_status = gr.Markdown("")
gr.Markdown("---")
gr.Markdown("## Step 2 of 2 — Connect Airtable (Your Dashboard & Data)")
gr.Markdown("""
<div class="setup-card">
<div class="step-header">📊 Airtable — Your Business Dashboard (Required)</div>
<div class="instructions">
Airtable stores all your data — customers, automations, social posts, logs. FREE for up to 1,000 records.
**How to get your token (3 minutes):**
**Part A — Get your Personal Access Token:**
1. Go to **[airtable.com/create/tokens](https://airtable.com/create/tokens)** (login first)
2. Click **+ Create token**
3. Name it: **ARIA v4**
4. Under **Scopes**, add ALL of these:
- `data.records:read`
- `data.records:write`
- `schema.bases:read`
- `schema.bases:write`
5. Under **Access**, select **All current and future bases**
6. Click **Create token** → Copy the token that starts with `pat...`
**Part B — Get your Base ID:**
1. Go to **[airtable.com](https://airtable.com)** and create a new Base (click **+ Add a base**)
2. Name it: **ARIA v4 Data**
3. Open that base — look at the URL bar. It looks like:
`https://airtable.com/appXXXXXXXXXXXXXX/...`
4. Copy everything after `/app` including "app" — that is your Base ID (starts with `app`)
</div>
</div>
""")
with gr.Row():
airtable_token_input = gr.Textbox(
placeholder="patXXXXXXXXXXXXXX.XXXXXXXX",
label="Airtable Personal Access Token",
type="password"
)
base_id_input = gr.Textbox(
placeholder="appXXXXXXXXXXXXXX",
label="Airtable Base ID"
)
airtable_btn = gr.Button("🔌 Connect Airtable", variant="primary")
airtable_status = gr.Markdown("")
gr.Markdown("---")
gr.Markdown("## ⚡ Optional — Add Paid APIs for More Power")
gr.Markdown("""
<div class="setup-card">
<div class="step-header">💎 Optional Upgrades</div>
<div class="instructions">
These are completely optional. ARIA works great with just Groq (free). Add these if you want extra capabilities:
| API | What it unlocks | Cost |
|-----|----------------|------|
| **OpenAI** | GPT-4o for complex tasks | ~$20/month |
| **ElevenLabs** | Human-quality voice generation | Free tier available |
| **Together AI** | Additional AI models | Free $5 credits |
Get OpenAI key at: **[platform.openai.com/api-keys](https://platform.openai.com/api-keys)**
</div>
</div>
""")
openai_input = gr.Textbox(
placeholder="sk-xxxxxxxxxxxxxxxxxx (optional — leave blank to skip)",
label="OpenAI API Key (Optional)",
type="password"
)
openai_btn = gr.Button("Add OpenAI (Optional)", variant="secondary")
openai_status = gr.Markdown("")
gr.Markdown("---")
setup_summary = gr.Markdown("⏳ Complete Steps 1 & 2 above to unlock ARIA Chat.")
go_to_chat_btn = gr.Button("🚀 I'm Ready — Go to ARIA Chat!", variant="primary", visible=False, size="lg")
# ════════════════════════════════
# TAB 2: ARIA CHAT
# ════════════════════════════════
with gr.Tab("🤖 ARIA Chat"):
gr.Markdown("### Chat with ARIA — Your Autonomous Business AI")
gr.Markdown("*Ask ARIA to write content, build strategies, plan campaigns, create email sequences, and more.*")
chatbot = gr.Chatbot(
type="messages",
height=500,
label="ARIA v4",
placeholder="Complete Setup (Tab 1) first, then start chatting!",
show_label=False
)
with gr.Row():
chat_input = gr.Textbox(
placeholder="Ask ARIA anything... e.g. 'Write 5 Instagram captions for my coaching business'",
label="",
scale=8
)
send_btn = gr.Button("Send →", scale=1, variant="primary")
gr.Markdown("""
**💡 Try these:**
- *"Write a week of LinkedIn posts for a business coach"*
- *"Create an email sequence for someone who downloaded my free guide"*
- *"Give me 10 TikTok hooks about productivity for entrepreneurs"*
- *"Build a 30-day content calendar for wellness products"*
- *"Write a product description for my $47 digital course"*
""")
# ════════════════════════════════
# TAB 3: HELP
# ════════════════════════════════
with gr.Tab("❓ Help & FAQ"):
gr.Markdown("""
## Frequently Asked Questions
**Q: Is my data safe?**
A: Yes. Your API keys are stored only in your browser session — they are never saved to any server. Each time you open ARIA, you enter them fresh.
**Q: What does Groq cost?**
A: Groq is FREE. Their free tier gives you 14,400 requests per day — more than enough for heavy daily use.
**Q: What does Airtable cost?**
A: Airtable is FREE for up to 1,000 records per base. That covers months of typical use. When you grow, their paid plan is $20/month.
**Q: Do I need OpenAI?**
A: No! ARIA runs perfectly on Groq (free). OpenAI is optional if you want GPT-4o for specific tasks.
**Q: My Groq key isn't working — what do I do?**
A: 1) Make sure you copied the full key (starts with `gsk_`). 2) Check you haven't hit the daily free limit. 3) Create a new key at console.groq.com.
**Q: My Airtable token isn't working — what do I do?**
A: 1) Make sure the token starts with `pat`. 2) Check that you added all 4 scopes. 3) Make sure the Base ID starts with `app` and matches your actual base.
**Q: Can I use ARIA on my phone?**
A: Yes! This app works in any mobile browser.
**Q: How do I get support?**
A: Email support is included with your purchase. Reply to your Gumroad receipt email.
""")
# ─────────────────────────────────────────────
# EVENT HANDLERS
# ─────────────────────────────────────────────
def handle_groq(key, groq_valid, airtable_valid):
ok, msg = validate_groq_key(key)
go_visible, summary, complete = check_setup_complete(ok, airtable_valid)
return msg, ok, key if ok else "", go_visible, summary, complete
def handle_airtable(token, base_id, groq_valid, airtable_valid):
ok, msg = validate_airtable_token(token, base_id)
go_visible, summary, complete = check_setup_complete(groq_valid, ok)
return msg, ok, token if ok else "", base_id if ok else "", go_visible, summary, complete
groq_btn.click(
handle_groq,
inputs=[groq_input, groq_valid_state, airtable_valid_state],
outputs=[groq_status, groq_valid_state, groq_key_state, go_to_chat_btn, setup_summary, setup_complete_state]
)
airtable_btn.click(
handle_airtable,
inputs=[airtable_token_input, base_id_input, groq_valid_state, airtable_valid_state],
outputs=[airtable_status, airtable_valid_state, airtable_token_state, base_id_state, go_to_chat_btn, setup_summary, setup_complete_state]
)
openai_btn.click(
lambda k: validate_openai_key(k)[1],
inputs=[openai_input],
outputs=[openai_status]
)
def send_chat(msg, history, groq_key, airtable_token, base_id, setup_complete):
if not msg.strip():
return history, gr.update(value="")
return chat_with_aria(msg, history, groq_key, airtable_token, base_id, setup_complete)
send_btn.click(
send_chat,
inputs=[chat_input, chatbot, groq_key_state, airtable_token_state, base_id_state, setup_complete_state],
outputs=[chatbot, chat_input]
)
chat_input.submit(
send_chat,
inputs=[chat_input, chatbot, groq_key_state, airtable_token_state, base_id_state, setup_complete_state],
outputs=[chatbot, chat_input]
)
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)