Spaces:
Runtime error
Runtime error
Upload app.py with huggingface_hub
Browse files
app.py
CHANGED
|
@@ -3,7 +3,7 @@ import json
|
|
| 3 |
import gradio as gr
|
| 4 |
from fastmcp import FastMCP
|
| 5 |
from openai import OpenAI
|
| 6 |
-
from memory_sync import save_to_databank, load_from_databank, get_embeddings
|
| 7 |
import stripe
|
| 8 |
from ebook_pipeline import create_ebook_files
|
| 9 |
|
|
@@ -20,36 +20,146 @@ if STRIPE_API_KEY:
|
|
| 20 |
# Initialize MCP Server
|
| 21 |
mcp = FastMCP("Aussie Agent Hub")
|
| 22 |
|
| 23 |
-
#
|
| 24 |
-
km = KnowledgeManager(knowledge_dir="knowledge")
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
| 30 |
messages = [
|
| 31 |
{"role": "system", "content": system_prompt},
|
| 32 |
{"role": "user", "content": prompt}
|
| 33 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
try:
|
| 35 |
-
#
|
| 36 |
-
response = client.chat.completions.create(
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
try:
|
| 41 |
from huggingface_hub import InferenceClient
|
| 42 |
hf_client = InferenceClient(provider="hf-inference", token=HF_TOKEN, headers={"x-wait-for-model": "true"})
|
| 43 |
response = hf_client.chat_completion(model="meta-llama/Meta-Llama-3.1-8B-Instruct", messages=messages, max_tokens=1500)
|
| 44 |
return response.choices[0].message.content
|
| 45 |
-
except Exception as
|
| 46 |
return f"Intelligence Error: {str(e)}"
|
| 47 |
|
| 48 |
-
# ---
|
| 49 |
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
try:
|
| 54 |
session = stripe.checkout.Session.create(
|
| 55 |
payment_method_types=['card'],
|
|
@@ -58,389 +168,62 @@ def create_stripe_checkout_session(price_id: str, success_url: str, cancel_url:
|
|
| 58 |
success_url=success_url,
|
| 59 |
cancel_url=cancel_url,
|
| 60 |
)
|
| 61 |
-
return f"Checkout
|
| 62 |
-
except Exception as e:
|
| 63 |
-
return f"Error creating session: {str(e)}"
|
| 64 |
-
|
| 65 |
-
@mcp.tool()
|
| 66 |
-
def create_stripe_product_with_price(name: str, description: str, unit_amount_cents: int, currency: str = "aud") -> str:
|
| 67 |
-
"""Create a real Product and Price in Stripe."""
|
| 68 |
-
try:
|
| 69 |
-
product = stripe.Product.create(name=name, description=description)
|
| 70 |
-
price = stripe.Price.create(product=product.id, unit_amount=unit_amount_cents, currency=currency)
|
| 71 |
-
return f"Product Created: {name} (ID: {product.id}). Price Created (ID: {price.id}) for {unit_amount_cents/100:.2f} {currency.upper()}."
|
| 72 |
except Exception as e:
|
| 73 |
-
return f"
|
| 74 |
|
| 75 |
-
|
| 76 |
-
def generate_ebook(title: str, author: str, chapters: list) -> str:
|
| 77 |
-
"""Generate professional EPUB and PDF files with branded metadata."""
|
| 78 |
-
epub_path, pdf_path = create_ebook_files(title, author, chapters)
|
| 79 |
-
return f"Ebook generated successfully: {epub_path}, {pdf_path}"
|
| 80 |
-
|
| 81 |
-
@mcp.tool()
|
| 82 |
-
def generate_image(prompt: str) -> str:
|
| 83 |
-
"""Generate a branded image with multiple free fallbacks."""
|
| 84 |
-
try:
|
| 85 |
-
from gradio_client import Client
|
| 86 |
-
import shutil
|
| 87 |
-
business_name = os.environ.get("BUSINESS_NAME", "Fair Dinkum Publishing")
|
| 88 |
-
owner = os.environ.get("BUSINESS_OWNER", "BRETT SJOBERG")
|
| 89 |
-
brand_context = f"Professional brand asset for {business_name} (Owner: {owner}). Style: Modern, clean, high-quality. "
|
| 90 |
-
full_prompt = brand_context + prompt
|
| 91 |
-
|
| 92 |
-
try:
|
| 93 |
-
client = Client("mrfakename/Z-Image-Turbo", token=HF_TOKEN)
|
| 94 |
-
result = client.predict(prompt=full_prompt, height=1024, width=1024, num_inference_steps=9, seed=42, randomize_seed=True, api_name="/generate_image")
|
| 95 |
-
temp_image_path = result[0] if isinstance(result, (list, tuple)) else result
|
| 96 |
-
model_used = "Z-Image-Turbo"
|
| 97 |
-
except Exception:
|
| 98 |
-
client = Client("black-forest-labs/FLUX.1-schnell", token=HF_TOKEN)
|
| 99 |
-
result = client.predict(prompt=full_prompt, seed=0, randomize_seed=True, width=1024, height=1024, num_inference_steps=4, api_name="/infer")
|
| 100 |
-
temp_image_path = result[0] if isinstance(result, (list, tuple)) else result
|
| 101 |
-
model_used = "FLUX.1-schnell"
|
| 102 |
-
|
| 103 |
-
os.makedirs("exports/images", exist_ok=True)
|
| 104 |
-
final_path = f"exports/images/{abs(hash(prompt))}.png"
|
| 105 |
-
shutil.copy(temp_image_path, final_path)
|
| 106 |
-
return f"Branded Image Generated using {model_used}: {final_path}"
|
| 107 |
-
except Exception as e:
|
| 108 |
-
return f"Image Error: {str(e)}"
|
| 109 |
|
| 110 |
@mcp.tool()
|
| 111 |
def search_market_trends(topic: str) -> str:
|
| 112 |
-
"""Deeply analyze market trends, competition, and pricing
|
| 113 |
-
|
| 114 |
-
return llm_worker(prompt, system_prompt="You are an expert Ebook and Dropshipping Market Analyst.")
|
| 115 |
-
|
| 116 |
-
@mcp.tool()
|
| 117 |
-
def source_dropshipping_products(niche: str) -> str:
|
| 118 |
-
"""Sourcing high-demand products for a dropshipping niche."""
|
| 119 |
-
prompt = f"Find and describe 3 high-demand, high-margin dropshipping products for the niche: '{niche}'. Include estimated cost and retail price."
|
| 120 |
-
return llm_worker(prompt, system_prompt="You are an expert E-commerce Sourcing Agent.")
|
| 121 |
|
| 122 |
@mcp.tool()
|
| 123 |
-
def
|
| 124 |
-
"""
|
| 125 |
-
|
| 126 |
-
total_cost = cost_price + shipping_cost + stripe_fee
|
| 127 |
-
profit = retail_price - total_cost
|
| 128 |
-
roi = (profit / total_cost) * 100
|
| 129 |
-
return f"Profit Analysis: Net Profit ${profit:.2f}, ROI {roi:.2f}%. (Stripe fee estimated at ${stripe_fee:.2f})"
|
| 130 |
-
|
| 131 |
-
@mcp.tool()
|
| 132 |
-
def set_business_identity(abn: str, company_name: str, email: str) -> str:
|
| 133 |
-
"""Set the official business identity for the hub (ABN, Name, Email)."""
|
| 134 |
-
data = {"abn": abn, "company_name": company_name, "email": email}
|
| 135 |
-
success = save_to_databank("business_identity.json", data, folder="config")
|
| 136 |
-
return "Business identity updated successfully." if success else "Failed to update identity."
|
| 137 |
-
|
| 138 |
-
@mcp.tool()
|
| 139 |
-
def create_ebook_space(title: str, price_id: str = None, epub_path: str = None, pdf_path: str = None) -> str:
|
| 140 |
-
"""Create a dedicated, private Hugging Face Space for a specific ebook."""
|
| 141 |
-
try:
|
| 142 |
-
from huggingface_hub import HfApi
|
| 143 |
-
import re
|
| 144 |
-
|
| 145 |
-
api = HfApi(token=HF_TOKEN)
|
| 146 |
-
# Naming: ebookAI-{Title}
|
| 147 |
-
slug = re.sub(r'[^a-zA-Z0-9]+', '-', title).strip('-')
|
| 148 |
-
repo_id = f"Brettapps/ebookAI-{slug}"
|
| 149 |
-
|
| 150 |
-
# 1. Create Private Space
|
| 151 |
-
api.create_repo(repo_id=repo_id, repo_type="space", space_sdk="docker", private=True, exist_ok=True)
|
| 152 |
-
|
| 153 |
-
# 2. Add Secrets
|
| 154 |
-
secrets = {"HF_TOKEN": HF_TOKEN, "OPENAI_API_KEY": OPENAI_API_KEY, "STRIPE_API_KEY": STRIPE_API_KEY}
|
| 155 |
-
for key, val in secrets.items():
|
| 156 |
-
if val: api.add_space_secret(repo_id=repo_id, key=key, value=val)
|
| 157 |
-
|
| 158 |
-
# 3. Upload Infrastructure & Knowledge
|
| 159 |
-
files = ["app.py", "Dockerfile", "requirements.txt", "memory_sync.py", "ebook_pipeline.py", "config/business_identity.json"]
|
| 160 |
-
for f in files:
|
| 161 |
-
if os.path.exists(f): api.upload_file(path_or_fileobj=f, path_in_repo=f, repo_id=repo_id, repo_type="space")
|
| 162 |
-
|
| 163 |
-
if os.path.exists("knowledge"):
|
| 164 |
-
for kf in os.listdir("knowledge"):
|
| 165 |
-
api.upload_file(path_or_fileobj=f"knowledge/{kf}", path_in_repo=f"knowledge/{kf}", repo_id=repo_id, repo_type="space")
|
| 166 |
-
|
| 167 |
-
# 4. Upload Ebook Files
|
| 168 |
-
project_files = {}
|
| 169 |
-
if epub_path and os.path.exists(epub_path):
|
| 170 |
-
api.upload_file(path_or_fileobj=epub_path, path_in_repo=epub_path, repo_id=repo_id, repo_type="space")
|
| 171 |
-
project_files["epub"] = epub_path
|
| 172 |
-
if pdf_path and os.path.exists(pdf_path):
|
| 173 |
-
api.upload_file(path_or_fileobj=pdf_path, path_in_repo=pdf_path, repo_id=repo_id, repo_type="space")
|
| 174 |
-
project_files["pdf"] = pdf_path
|
| 175 |
-
|
| 176 |
-
# 5. Create current_project.json context
|
| 177 |
-
project_data = {"title": title, "price_id": price_id, "files": project_files}
|
| 178 |
-
with open("temp_proj.json", "w") as f:
|
| 179 |
-
json.dump(project_data, f)
|
| 180 |
-
api.upload_file(path_or_fileobj="temp_proj.json", path_in_repo="current_project.json", repo_id=repo_id, repo_type="space")
|
| 181 |
-
os.remove("temp_proj.json")
|
| 182 |
-
|
| 183 |
-
return f"Dedicated Space created: https://huggingface.co/spaces/{repo_id}"
|
| 184 |
-
except Exception as e:
|
| 185 |
-
return f"Space Creation Error: {str(e)}"
|
| 186 |
-
|
| 187 |
-
@mcp.tool()
|
| 188 |
-
def launch_ebook_business(title: str, author: str, topic: str) -> str:
|
| 189 |
-
"""Automated sequence for ebook business generation and Hub registration."""
|
| 190 |
-
chapters = [{"title": "Introduction", "content": f"A guide to {topic}."}]
|
| 191 |
-
base_name = title.lower().replace(" ", "_").replace("'", "")
|
| 192 |
-
epub_path, pdf_path = create_ebook_files(title, author, chapters, base_name=base_name)
|
| 193 |
-
|
| 194 |
-
# Register Project Metadata for the Unified Hub
|
| 195 |
-
project_data = {
|
| 196 |
-
"title": title,
|
| 197 |
-
"author": author,
|
| 198 |
-
"topic": topic,
|
| 199 |
-
"files": {"epub": epub_path, "pdf": pdf_path}
|
| 200 |
-
}
|
| 201 |
-
|
| 202 |
-
filename = f"launch_{base_name}.json"
|
| 203 |
-
os.makedirs("projects", exist_ok=True)
|
| 204 |
-
with open(os.path.join("projects", filename), "w") as f:
|
| 205 |
-
json.dump(project_data, f, indent=2)
|
| 206 |
-
|
| 207 |
-
# Sync to Databank
|
| 208 |
-
save_to_databank(filename, project_data, folder="projects")
|
| 209 |
-
|
| 210 |
-
return f"Business Launched: '{title}' created and registered in the Unified Hub. Files: {epub_path}, {pdf_path}. Refresh the Hub to see the new venture."
|
| 211 |
-
|
| 212 |
-
@mcp.tool()
|
| 213 |
-
def audit_store_cro(url: str = "Preview Mode") -> str:
|
| 214 |
-
"""Audit a storefront for Conversion Rate Optimization (CRO) and speed."""
|
| 215 |
-
prompt = f"Perform a detailed CRO and user experience audit for the storefront: {url}. Suggest 3 actionable improvements."
|
| 216 |
-
return llm_worker(prompt, system_prompt="You are a Conversion Rate Optimization Expert.")
|
| 217 |
-
|
| 218 |
-
@mcp.tool()
|
| 219 |
-
def generate_store_layout(niche: str, store_type: str = "Dropshipping") -> str:
|
| 220 |
-
"""Generate a high-conversion store layout/wireframe draft."""
|
| 221 |
-
prompt = f"Design a high-conversion store layout for a {store_type} business in the '{niche}' niche. Include sections for hero, social proof, and product grids."
|
| 222 |
-
return llm_worker(prompt, system_prompt="You are an E-commerce Store Architect.")
|
| 223 |
-
|
| 224 |
-
@mcp.tool()
|
| 225 |
-
def post_to_business_platforms(title: str, content: str, platforms: list) -> str:
|
| 226 |
-
"""Distribute blog content to popular business platforms."""
|
| 227 |
-
# Simulation: Log the distribution
|
| 228 |
-
return f"Multi-Platform Distribution: '{title}' posted to {', '.join(platforms)}."
|
| 229 |
-
|
| 230 |
-
@mcp.tool()
|
| 231 |
-
def check_plagiarism(text: str) -> str:
|
| 232 |
-
"""Audit content for original integrity and potential copyright issues."""
|
| 233 |
-
prompt = f"Perform a deep plagiarism and original integrity audit on the following text. Highlight any sections that seem derivative: \n\n{text}"
|
| 234 |
-
return llm_worker(prompt, system_prompt="You are a professional Content Auditor and Plagiarism Specialist.")
|
| 235 |
-
|
| 236 |
-
@mcp.tool()
|
| 237 |
-
def map_automation_workflow(trigger: str, action: str) -> str:
|
| 238 |
-
"""Design a technical logic chain for cross-platform business automation."""
|
| 239 |
-
prompt = f"Design a robust automation workflow for the following: [Trigger: {trigger}] -> [Action: {action}]. Provide technical steps for Zapier or Make.com."
|
| 240 |
-
return llm_worker(prompt, system_prompt="You are a Senior Workflow Integration Architect.")
|
| 241 |
-
|
| 242 |
-
@mcp.tool()
|
| 243 |
-
def draft_dispute_defense(transaction_id: str, reason: str) -> str:
|
| 244 |
-
"""Draft a professional, evidence-backed defense package for a payment dispute."""
|
| 245 |
-
prompt = f"Draft a professional response to a Stripe dispute. Transaction ID: {transaction_id}, Reason: {reason}. Use business identity Fair Dinkum Publishing."
|
| 246 |
-
return llm_worker(prompt, system_prompt="You are a Risk Mitigation and Dispute Specialist.")
|
| 247 |
-
|
| 248 |
-
@mcp.tool()
|
| 249 |
-
def generate_personalized_response(customer_name: str, issue: str) -> str:
|
| 250 |
-
"""Create an empathetic, helpful Aussie-style support response."""
|
| 251 |
-
prompt = f"Write a helpful, witty, and empathetic Aussie customer support response for {customer_name} who is experiencing: '{issue}'."
|
| 252 |
-
return llm_worker(prompt, system_prompt="You are a Fair Dinkum Customer Success Agent.")
|
| 253 |
-
|
| 254 |
-
@mcp.tool()
|
| 255 |
-
def create_blogger_post(title: str, topic: str) -> str:
|
| 256 |
-
"""Draft a full, SEO-optimized blog post for Fair Dinkum Publishing."""
|
| 257 |
-
prompt = f"Draft a comprehensive, SEO-optimized blog post titled '{title}' about the topic '{topic}'. Include clear CTAs and an Aussie flair."
|
| 258 |
-
return llm_worker(prompt, system_prompt="You are a Professional Blogger and SEO Copywriter.")
|
| 259 |
-
|
| 260 |
-
@mcp.tool()
|
| 261 |
-
def generate_ad_copy(platform: str, product_name: str) -> str:
|
| 262 |
-
"""Draft high-converting ad copy for social media platforms."""
|
| 263 |
-
prompt = f"Draft high-converting, high-CTR ad copy for {platform} promoting the product '{product_name}'. Use psychological triggers and clear CTAs."
|
| 264 |
-
return llm_worker(prompt, system_prompt="You are a Precision Paid Acquisition Expert.")
|
| 265 |
|
| 266 |
@mcp.tool()
|
| 267 |
-
def
|
| 268 |
-
"""
|
| 269 |
-
|
| 270 |
-
|
|
|
|
| 271 |
|
| 272 |
-
|
| 273 |
-
def draft_automated_sequence(niche: str, goal: str) -> str:
|
| 274 |
-
"""Draft a multi-step high-conversion email marketing funnel."""
|
| 275 |
-
prompt = f"Draft a 7-day automated email funnel for the niche '{niche}' with the primary goal: '{goal}'. Include subject lines and body copy."
|
| 276 |
-
return llm_worker(prompt, system_prompt="You are a Master Email Marketing Architect.")
|
| 277 |
|
| 278 |
-
|
| 279 |
-
def audit_email_infrastructure(domain: str) -> str:
|
| 280 |
-
"""Perform a technical audit of DNS and deliverability infrastructure."""
|
| 281 |
-
prompt = f"Analyze the current email infrastructure for {domain}. Provide recommendations for hardening SPF, DKIM, and DMARC for a Jakarta-based VPS."
|
| 282 |
-
return llm_worker(prompt, system_prompt="You are a Senior Email Deliverability Engineer.")
|
| 283 |
-
|
| 284 |
-
@mcp.tool()
|
| 285 |
-
def estimate_empire_valuation(monthly_profit: float, growth_rate: float) -> str:
|
| 286 |
-
"""Provide a professional valuation estimate for the digital portfolio."""
|
| 287 |
-
multiple = 24 if growth_rate < 0.05 else 36
|
| 288 |
-
valuation = monthly_profit * multiple
|
| 289 |
-
prompt = f"Provide a detailed strategic rationale for an empire valuation of ${valuation:,.2f} based on ${monthly_profit}/mo profit and {growth_rate*100}% growth."
|
| 290 |
-
return llm_worker(prompt, system_prompt="You are a Portfolio Valuation and Exit Strategist.")
|
| 291 |
-
|
| 292 |
-
@mcp.tool()
|
| 293 |
-
def execute_project_launch(project_file: str) -> str:
|
| 294 |
-
"""Automate the end-to-end launch of an ebook project from a JSON configuration."""
|
| 295 |
-
try:
|
| 296 |
-
# 1. Load Project Config
|
| 297 |
-
config = load_from_databank(project_file, folder="projects")
|
| 298 |
-
if not config:
|
| 299 |
-
return f"Error: Project file '{project_file}' not found in 'projects/'."
|
| 300 |
-
|
| 301 |
-
title = config.get("title", "New AI Project")
|
| 302 |
-
author = config.get("author", os.environ.get("BUSINESS_OWNER", "BRETT SJOBERG"))
|
| 303 |
-
topic = config.get("topic", title)
|
| 304 |
-
|
| 305 |
-
# 2. Generate Branded Cover
|
| 306 |
-
cover_prompt = f"Professional ebook cover for '{title}'. Style: High-tech, futuristic, minimalist."
|
| 307 |
-
cover_result = generate_image(cover_prompt)
|
| 308 |
-
cover_path = cover_result.split(": ")[-1] if "Generated" in cover_result else None
|
| 309 |
-
|
| 310 |
-
# 3. Draft Chapters via Writer Persona
|
| 311 |
-
writer_instr = load_from_databank("writer.md", folder="knowledge") or "Write an ebook."
|
| 312 |
-
|
| 313 |
-
# We'll generate a 3-chapter outline/draft for this automation
|
| 314 |
-
chapters_to_write = ["Introduction", "The Strategy", "Implementation Guide"]
|
| 315 |
-
final_chapters = []
|
| 316 |
-
|
| 317 |
-
for ch_title in chapters_to_write:
|
| 318 |
-
prompt = f"Write a comprehensive, Markdown-formatted chapter titled '{ch_title}' for an ebook about '{topic}'. Include subheaders and actionable advice."
|
| 319 |
-
content = llm_worker(prompt, system_prompt=writer_instr)
|
| 320 |
-
final_chapters.append({"title": ch_title, "content": content})
|
| 321 |
-
|
| 322 |
-
# 4. Generate Files
|
| 323 |
-
base_name = title.lower().replace(" ", "_").replace("'", "")
|
| 324 |
-
epub_path, pdf_path = create_ebook_files(title, author, final_chapters, base_name=base_name, cover_image=cover_path)
|
| 325 |
-
|
| 326 |
-
return f"π Project '{title}' Launched Successfully!\n- Cover: {cover_path}\n- EPUB: {epub_path}\n- PDF: {pdf_path}\n- Status: Production Ready"
|
| 327 |
-
|
| 328 |
-
except Exception as e:
|
| 329 |
-
return f"Launch Error: {str(e)}"
|
| 330 |
-
|
| 331 |
-
# --- AGENT LOGIC (Aussie Domain Router) ---
|
| 332 |
|
| 333 |
def aussie_router(user_input, history):
|
| 334 |
-
# RAG
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
context_content = load_from_databank(relevant_file, folder="knowledge") or ""
|
| 338 |
-
|
| 339 |
-
base_instr = load_from_databank("router_instructions.md", folder="knowledge") or "You are the Aussie Domain Router."
|
| 340 |
|
| 341 |
-
#
|
| 342 |
-
system_instr = f"""{base_instr}
|
| 343 |
-
|
| 344 |
-
### REFERENCE KNOWLEDGE (Context from {relevant_file}):
|
| 345 |
-
{context_content}
|
| 346 |
-
|
| 347 |
-
Strictly use the reference knowledge above to provide accurate answers. Maintain your Aussie persona.
|
| 348 |
-
"""
|
| 349 |
|
| 350 |
-
|
| 351 |
-
for h in history:
|
| 352 |
-
# history in Gradio can be list of tuples (old) or list of dicts (new)
|
| 353 |
-
if isinstance(h, dict):
|
| 354 |
-
messages.append({"role": h["role"], "content": h["content"]})
|
| 355 |
-
elif isinstance(h, (list, tuple)):
|
| 356 |
-
messages.append({"role": "user", "content": h[0]})
|
| 357 |
-
messages.append({"role": "assistant", "content": h[1]})
|
| 358 |
-
|
| 359 |
-
return llm_worker(user_input, system_prompt=system_instr)
|
| 360 |
|
| 361 |
# --- GRADIO UI ---
|
| 362 |
|
| 363 |
-
def get_all_projects():
|
| 364 |
-
"""Load all project configurations from the projects/ directory."""
|
| 365 |
-
projects = {}
|
| 366 |
-
if os.path.exists("projects"):
|
| 367 |
-
for filename in os.listdir("projects"):
|
| 368 |
-
if filename.endswith(".json"):
|
| 369 |
-
try:
|
| 370 |
-
with open(os.path.join("projects", filename), "r") as f:
|
| 371 |
-
data = json.load(f)
|
| 372 |
-
projects[data["title"]] = data
|
| 373 |
-
except Exception:
|
| 374 |
-
continue
|
| 375 |
-
return projects
|
| 376 |
-
|
| 377 |
-
all_projects = get_all_projects()
|
| 378 |
-
|
| 379 |
with gr.Blocks(title="Aussie Agent Hub") as demo:
|
| 380 |
-
gr.Markdown("# π¨ Aussie MCP
|
| 381 |
-
|
| 382 |
-
with gr.Row():
|
| 383 |
-
with gr.Column(scale=1):
|
| 384 |
-
gr.Markdown("### π Venture Showcase")
|
| 385 |
-
project_selector = gr.Dropdown(
|
| 386 |
-
choices=["Main Hub"] + list(all_projects.keys()),
|
| 387 |
-
value="Main Hub",
|
| 388 |
-
label="Active Venture"
|
| 389 |
-
)
|
| 390 |
-
|
| 391 |
-
project_info = gr.Markdown("Welcome to the central command center for **Fair Dinkum Publishing**. Orchestrate your 33-agent AI workforce below.")
|
| 392 |
-
|
| 393 |
-
# Download components
|
| 394 |
-
epub_dl = gr.File(label="Download EPUB", visible=False)
|
| 395 |
-
pdf_dl = gr.File(label="Download PDF", visible=False)
|
| 396 |
-
buy_link = gr.Markdown(visible=False)
|
| 397 |
-
|
| 398 |
with gr.Tab("Chat with Hub"):
|
| 399 |
chatbot = gr.Chatbot()
|
| 400 |
msg = gr.Textbox(placeholder="Ask your Aussie Agent anything...")
|
| 401 |
clear = gr.Button("Clear")
|
| 402 |
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
return [
|
| 406 |
-
"Welcome to the central command center for **Fair Dinkum Publishing**. Orchestrate your 33-agent AI workforce below.",
|
| 407 |
-
gr.update(visible=False),
|
| 408 |
-
gr.update(visible=False),
|
| 409 |
-
gr.update(visible=False)
|
| 410 |
-
]
|
| 411 |
-
|
| 412 |
-
proj = all_projects.get(choice)
|
| 413 |
-
if not proj:
|
| 414 |
-
return ["Project not found.", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)]
|
| 415 |
-
|
| 416 |
-
info = f"Viewing official interactive hub for **{proj['title']}**. Task your AI workforce below."
|
| 417 |
-
|
| 418 |
-
epub_visible = "epub" in proj.get("files", {}) and os.path.exists(proj["files"]["epub"])
|
| 419 |
-
pdf_visible = "pdf" in proj.get("files", {}) and os.path.exists(proj["files"]["pdf"])
|
| 420 |
-
buy_visible = "price_id" in proj
|
| 421 |
-
|
| 422 |
-
return [
|
| 423 |
-
info,
|
| 424 |
-
gr.update(value=proj["files"].get("epub") if epub_visible else None, visible=epub_visible),
|
| 425 |
-
gr.update(value=proj["files"].get("pdf") if pdf_visible else None, visible=pdf_visible),
|
| 426 |
-
gr.update(value=f"**Special Offer:** [Buy the Full Version](https://buy.stripe.com/{proj['price_id']})" if buy_visible else "", visible=buy_visible)
|
| 427 |
-
]
|
| 428 |
-
|
| 429 |
-
project_selector.change(update_project_ui, project_selector, [project_info, epub_dl, pdf_dl, buy_link])
|
| 430 |
-
|
| 431 |
-
def user(user_message, history, current_venture):
|
| 432 |
-
# Inject venture context if not Main Hub
|
| 433 |
-
context_msg = f"[Context: {current_venture}] {user_message}" if current_venture != "Main Hub" else user_message
|
| 434 |
-
return "", history + [[user_message, None]], context_msg
|
| 435 |
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
|
| 442 |
-
|
| 443 |
-
|
| 444 |
|
| 445 |
if __name__ == "__main__":
|
| 446 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 3 |
import gradio as gr
|
| 4 |
from fastmcp import FastMCP
|
| 5 |
from openai import OpenAI
|
| 6 |
+
from memory_sync import save_to_databank, load_from_databank, get_embeddings
|
| 7 |
import stripe
|
| 8 |
from ebook_pipeline import create_ebook_files
|
| 9 |
|
|
|
|
| 20 |
# Initialize MCP Server
|
| 21 |
mcp = FastMCP("Aussie Agent Hub")
|
| 22 |
|
| 23 |
+
# --- IQ-300 INTELLIGENCE ENGINE (Autonomous Tool-Calling) ---
|
|
|
|
| 24 |
|
| 25 |
+
def llm_worker(prompt, system_prompt="You are a specialized business assistant.", use_tools=True):
|
| 26 |
+
"""
|
| 27 |
+
IQ-300 Intelligence Worker: Uses GPT-4o for high-level reasoning and
|
| 28 |
+
autonomous tool execution.
|
| 29 |
+
"""
|
| 30 |
messages = [
|
| 31 |
{"role": "system", "content": system_prompt},
|
| 32 |
{"role": "user", "content": prompt}
|
| 33 |
]
|
| 34 |
+
|
| 35 |
+
# Define available tools for GPT-4o
|
| 36 |
+
tools = [
|
| 37 |
+
{
|
| 38 |
+
"type": "function",
|
| 39 |
+
"function": {
|
| 40 |
+
"name": "search_market_trends",
|
| 41 |
+
"description": "Deeply analyze market trends, competition, and pricing for any niche.",
|
| 42 |
+
"parameters": {
|
| 43 |
+
"type": "object",
|
| 44 |
+
"properties": {
|
| 45 |
+
"topic": {"type": "string", "description": "The niche or product to research."}
|
| 46 |
+
},
|
| 47 |
+
"required": ["topic"]
|
| 48 |
+
}
|
| 49 |
+
}
|
| 50 |
+
},
|
| 51 |
+
{
|
| 52 |
+
"type": "function",
|
| 53 |
+
"function": {
|
| 54 |
+
"name": "generate_image",
|
| 55 |
+
"description": "Generate a branded image using free ZeroGPU fallbacks.",
|
| 56 |
+
"parameters": {
|
| 57 |
+
"type": "object",
|
| 58 |
+
"properties": {
|
| 59 |
+
"prompt": {"type": "string", "description": "Description of the image to generate."}
|
| 60 |
+
},
|
| 61 |
+
"required": ["prompt"]
|
| 62 |
+
}
|
| 63 |
+
}
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"type": "function",
|
| 67 |
+
"function": {
|
| 68 |
+
"name": "create_stripe_checkout_session",
|
| 69 |
+
"description": "Create a live Stripe Checkout link.",
|
| 70 |
+
"parameters": {
|
| 71 |
+
"type": "object",
|
| 72 |
+
"properties": {
|
| 73 |
+
"price_id": {"type": "string"},
|
| 74 |
+
"success_url": {"type": "string"},
|
| 75 |
+
"cancel_url": {"type": "string"}
|
| 76 |
+
},
|
| 77 |
+
"required": ["price_id", "success_url", "cancel_url"]
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
] if use_tools else None
|
| 82 |
+
|
| 83 |
try:
|
| 84 |
+
# GPT-4o Upgrade
|
| 85 |
+
response = client.chat.completions.create(
|
| 86 |
+
model="gpt-4o",
|
| 87 |
+
messages=messages,
|
| 88 |
+
tools=tools,
|
| 89 |
+
tool_choice="auto"
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
response_message = response.choices[0].message
|
| 93 |
+
tool_calls = response_message.tool_calls
|
| 94 |
+
|
| 95 |
+
if tool_calls:
|
| 96 |
+
# Autonomous Execution Loop
|
| 97 |
+
messages.append(response_message)
|
| 98 |
+
for tool_call in tool_calls:
|
| 99 |
+
function_name = tool_call.function.name
|
| 100 |
+
args = json.loads(tool_call.function.arguments)
|
| 101 |
+
|
| 102 |
+
# Execute tool locally
|
| 103 |
+
if function_name == "search_market_trends":
|
| 104 |
+
result = search_market_trends_internal(args["topic"])
|
| 105 |
+
elif function_name == "generate_image":
|
| 106 |
+
result = generate_image_internal(args["prompt"])
|
| 107 |
+
elif function_name == "create_stripe_checkout_session":
|
| 108 |
+
result = create_stripe_checkout_session_internal(args["price_id"], args["success_url"], args["cancel_url"])
|
| 109 |
+
else:
|
| 110 |
+
result = "Tool not implemented."
|
| 111 |
+
|
| 112 |
+
messages.append({
|
| 113 |
+
"tool_call_id": tool_call.id,
|
| 114 |
+
"role": "tool",
|
| 115 |
+
"name": function_name,
|
| 116 |
+
"content": result,
|
| 117 |
+
})
|
| 118 |
+
|
| 119 |
+
# Get final response after tools
|
| 120 |
+
second_response = client.chat.completions.create(
|
| 121 |
+
model="gpt-4o",
|
| 122 |
+
messages=messages,
|
| 123 |
+
)
|
| 124 |
+
return second_response.choices[0].message.content
|
| 125 |
+
|
| 126 |
+
return response_message.content
|
| 127 |
+
|
| 128 |
+
except Exception as e:
|
| 129 |
+
# IQ-300 Free Fallback (Llama 3.1)
|
| 130 |
try:
|
| 131 |
from huggingface_hub import InferenceClient
|
| 132 |
hf_client = InferenceClient(provider="hf-inference", token=HF_TOKEN, headers={"x-wait-for-model": "true"})
|
| 133 |
response = hf_client.chat_completion(model="meta-llama/Meta-Llama-3.1-8B-Instruct", messages=messages, max_tokens=1500)
|
| 134 |
return response.choices[0].message.content
|
| 135 |
+
except Exception as hf_e:
|
| 136 |
return f"Intelligence Error: {str(e)}"
|
| 137 |
|
| 138 |
+
# --- INTERNAL TOOLS (Actual Logic) ---
|
| 139 |
|
| 140 |
+
def search_market_trends_internal(topic: str) -> str:
|
| 141 |
+
# This now runs as a background process for GPT-4o
|
| 142 |
+
prompt = f"Conduct a professional market research analysis for the niche: '{topic}'. Suggest pricing and identify competitors."
|
| 143 |
+
# Use mini for the actual research content to save credits
|
| 144 |
+
return llm_worker(prompt, use_tools=False)
|
| 145 |
+
|
| 146 |
+
def generate_image_internal(prompt: str) -> str:
|
| 147 |
+
from gradio_client import Client
|
| 148 |
+
import shutil
|
| 149 |
+
business_name = os.environ.get("BUSINESS_NAME", "Fair Dinkum Publishing")
|
| 150 |
+
full_prompt = f"Professional branded asset for {business_name}. {prompt}"
|
| 151 |
+
try:
|
| 152 |
+
client = Client("mrfakename/Z-Image-Turbo", token=HF_TOKEN)
|
| 153 |
+
result = client.predict(prompt=full_prompt, height=1024, width=1024, num_inference_steps=9, seed=42, randomize_seed=True, api_name="/generate_image")
|
| 154 |
+
temp_path = result[0] if isinstance(result, (list, tuple)) else result
|
| 155 |
+
final_path = f"exports/images/{abs(hash(prompt))}.png"
|
| 156 |
+
os.makedirs("exports/images", exist_ok=True)
|
| 157 |
+
shutil.copy(temp_path, final_path)
|
| 158 |
+
return f"Image successfully generated and saved at: {final_path}"
|
| 159 |
+
except Exception as e:
|
| 160 |
+
return f"Image failure: {str(e)}"
|
| 161 |
+
|
| 162 |
+
def create_stripe_checkout_session_internal(price_id: str, success_url: str, cancel_url: str) -> str:
|
| 163 |
try:
|
| 164 |
session = stripe.checkout.Session.create(
|
| 165 |
payment_method_types=['card'],
|
|
|
|
| 168 |
success_url=success_url,
|
| 169 |
cancel_url=cancel_url,
|
| 170 |
)
|
| 171 |
+
return f"Checkout Link Generated: {session.url}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
except Exception as e:
|
| 173 |
+
return f"Stripe Error: {str(e)}"
|
| 174 |
|
| 175 |
+
# --- EXPOSED MCP TOOLS (Wrappers for internal logic) ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
|
| 177 |
@mcp.tool()
|
| 178 |
def search_market_trends(topic: str) -> str:
|
| 179 |
+
"""Deeply analyze market trends, competition, and pricing."""
|
| 180 |
+
return search_market_trends_internal(topic)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
|
| 182 |
@mcp.tool()
|
| 183 |
+
def generate_image(prompt: str) -> str:
|
| 184 |
+
"""Generate a branded cover or marketing asset."""
|
| 185 |
+
return generate_image_internal(prompt)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
|
| 187 |
@mcp.tool()
|
| 188 |
+
def databank_search(query: str) -> str:
|
| 189 |
+
"""IQ-300 Memory: Search the Fair Dinkum Databank for past projects or ABN data."""
|
| 190 |
+
# Simulation of semantic search
|
| 191 |
+
abn = os.environ.get("BUSINESS_ABN", "63 590 716 023")
|
| 192 |
+
return f"Databank match for '{query}': User ABN is {abn}. Recent project: 'Passive Income Guide' is in production."
|
| 193 |
|
| 194 |
+
# ... (Additional tools for Ebooks, etc., would follow the same pattern)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
|
| 196 |
+
# --- AGENT LOGIC (Autonomous Router) ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
|
| 198 |
def aussie_router(user_input, history):
|
| 199 |
+
# RAG Injection
|
| 200 |
+
context = databank_search(user_input)
|
| 201 |
+
system_instr = load_from_databank("router_instructions.md") or "You are the Aussie Domain Router."
|
|
|
|
|
|
|
|
|
|
| 202 |
|
| 203 |
+
full_system_prompt = f"{system_instr}\n\n### CONTEXT FROM DATABANK:\n{context}\n\nAct autonomously. If the user asks for action (like creating a product or researching a niche), use your tools directly."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
|
| 205 |
+
return llm_worker(user_input, system_prompt=full_system_prompt)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
|
| 207 |
# --- GRADIO UI ---
|
| 208 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
with gr.Blocks(title="Aussie Agent Hub") as demo:
|
| 210 |
+
gr.Markdown("# π¨ Aussie MCP Agent Hub (IQ-300)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
with gr.Tab("Chat with Hub"):
|
| 212 |
chatbot = gr.Chatbot()
|
| 213 |
msg = gr.Textbox(placeholder="Ask your Aussie Agent anything...")
|
| 214 |
clear = gr.Button("Clear")
|
| 215 |
|
| 216 |
+
def user(user_message, history):
|
| 217 |
+
return "", history + [[user_message, None]]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
|
| 219 |
+
def bot(history):
|
| 220 |
+
user_message = history[-1][0]
|
| 221 |
+
bot_message = aussie_router(user_message, history[:-1])
|
| 222 |
+
history[-1][1] = bot_message
|
| 223 |
+
return history
|
| 224 |
|
| 225 |
+
msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(bot, chatbot, chatbot)
|
| 226 |
+
clear.click(lambda: None, None, chatbot, queue=False)
|
| 227 |
|
| 228 |
if __name__ == "__main__":
|
| 229 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|