""" 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("""
🧠 Groq — Your Free AI Brain (Required)
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!
""") 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("""
📊 Airtable — Your Business Dashboard (Required)
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`)
""") 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("""
💎 Optional Upgrades
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)**
""") 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)