File size: 18,538 Bytes
f6c63cd
937704d
0b3a222
f6c63cd
 
 
c81c133
819e82c
f6c63cd
 
 
 
 
819e82c
f6c63cd
819e82c
f6c63cd
819e82c
 
f6c63cd
3139bb9
 
f6c63cd
0b3a222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c81c133
937704d
9cf74fb
c81c133
 
9cf74fb
c81c133
9cf74fb
 
 
3920eb7
9cf74fb
3920eb7
 
c81c133
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d97938c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0b3a222
 
d97938c
 
 
0b3a222
d97938c
0b3a222
d97938c
 
 
c81c133
 
 
0b3a222
 
c81c133
 
 
0b3a222
 
 
c81c133
0b3a222
c81c133
 
 
 
 
 
0b3a222
 
c81c133
 
 
0b3a222
 
c81c133
0b3a222
c81c133
 
 
 
 
3920eb7
c81c133
df752f3
 
 
 
 
 
 
 
 
c81c133
 
 
 
 
 
 
 
 
 
 
 
 
 
0b3a222
 
 
 
c81c133
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3920eb7
 
 
 
 
c81c133
3920eb7
 
c81c133
f6c63cd
c81c133
 
0b3a222
c81c133
 
 
 
c2e116b
c81c133
c2e116b
 
 
0b3a222
c2e116b
 
c81c133
 
 
 
c2e116b
 
 
 
 
 
 
 
 
 
0b3a222
c81c133
d97938c
 
 
 
0b3a222
819e82c
c81c133
819e82c
df752f3
 
 
 
 
 
 
0b3a222
5a005ad
df752f3
 
 
 
 
5a005ad
 
c81c133
 
2cd5c56
d97938c
 
 
 
 
 
0b3a222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8c3ed27
2cd5c56
c81c133
 
 
8da4edb
34d68d6
0b3a222
 
 
 
 
 
8c3ed27
8da4edb
c81c133
0b3a222
c81c133
0b3a222
9cf74fb
c81c133
f6c63cd
 
c81c133
 
9cf74fb
937704d
0b3a222
937704d
c81c133
f6c63cd
 
 
0b3a222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36dd253
df752f3
0b3a222
 
 
 
 
 
 
 
 
df752f3
0b3a222
 
 
 
f6c63cd
2cd5c56
f6c63cd
 
 
0b3a222
 
 
 
 
 
 
 
 
df752f3
 
 
 
0b3a222
 
 
 
 
df752f3
0b3a222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f6c63cd
 
 
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
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
import os
import json
import time
import gradio as gr
from fastmcp import FastMCP
from openai import OpenAI
from memory_sync import save_to_databank, load_from_databank, get_embeddings
import stripe
from ebook_pipeline import create_ebook_files

# Load Environment Variables
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
HF_TOKEN = os.environ.get("HF_TOKEN")
STRIPE_API_KEY = os.environ.get("STRIPE_API_KEY")

# Initialize Clients
client = OpenAI(api_key=OPENAI_API_KEY)
if STRIPE_API_KEY:
    stripe.api_key = STRIPE_API_KEY

# Initialize MCP Server
mcp = FastMCP("Aussie Agent Hub")

# --- AWS TOOLS ---

@mcp.tool()
def deploy_to_sagemaker(model_id: str, instance_type: str = "ml.g5.xlarge", region: str = "ap-southeast-2") -> str:
    """Deploy a Hugging Face model to an AWS SageMaker real-time endpoint."""
    try:
        import boto3
        import sagemaker
        from sagemaker.huggingface import HuggingFaceModel
        
        if not os.environ.get("AWS_ACCESS_KEY_ID"):
            return "❌ Error: AWS credentials not found in environment secrets."
            
        role = os.environ.get("AWS_SAGEMAKER_ROLE")
        if not role:
            return "❌ Error: AWS_SAGEMAKER_ROLE secret is required for SageMaker deployment."

        session = sagemaker.Session(boto_session=boto3.Session(region_name=region))
        huggingface_model = HuggingFaceModel(
            env={'HF_MODEL_ID': model_id, 'HF_TASK': 'text-generation'},
            role=role,
            transformers_version="4.37.0",
            pytorch_version="2.1.0",
            py_version="py310",
        )
        predictor = huggingface_model.deploy(
            initial_instance_count=1,
            instance_type=instance_type,
            endpoint_name=f"aussie-hub-{model_id.split('/')[-1]}-{int(time.time())}"
        )
        return f"βœ… Deployment Successful! SageMaker Endpoint: {predictor.endpoint_name} is spinning up in {region}."
    except Exception as e:
        return f"❌ SageMaker Error: {str(e)}"

@mcp.tool()
def call_bedrock_intelligence(prompt: str, model_id: str = "anthropic.claude-3-5-sonnet-20240620-v1:0") -> str:
    """Query a high-performance model via AWS Bedrock for enterprise-grade intelligence."""
    try:
        import boto3
        region = "us-east-1"
        bedrock_client = boto3.client(service_name='bedrock-runtime', region_name=region)
        body = json.dumps({
            "anthropic_version": "bedrock-2023-05-31",
            "max_tokens": 1000,
            "messages": [{"role": "user", "content": prompt}]
        })
        response = bedrock_client.invoke_model(body=body, modelId=model_id)
        response_body = json.loads(response.get('body').read())
        return response_body.get('content')[0].get('text')
    except Exception as e:
        return f"❌ Bedrock Error: {str(e)}"

# --- IQ-200 FEW-SHOT INTELLIGENCE (No-Cost Context) ---

def load_examples(persona="router"):
    """Load Master Examples to provide few-shot intelligence to the model."""
    filename = f"{persona}_master_examples.md"
    try:
        # Check local folder first
        local_path = os.path.join("knowledge/examples", filename)
        if os.path.exists(local_path):
            with open(local_path, "r") as f:
                content = f.read()
        else:
            content = load_from_databank(filename, folder="knowledge/examples")
            
        return f"\n### MASTER EXAMPLES (IQ-200 Reference):\n{content}\n" if content else ""
    except Exception:
        return ""

# --- IQ-300 INTELLIGENCE ENGINE (Autonomous Tool-Calling) ---

def llm_worker(prompt, system_prompt="You are a specialized business assistant.", use_tools=True, persona=None):
    """
    IQ-300 Intelligence Worker: Uses GPT-4o for high-level reasoning and 
    autonomous tool execution. Now with Few-Shot Persona intelligence.
    """
    examples = load_examples(persona) if persona else ""
    full_system_prompt = f"{system_prompt}\n{examples}"
    
    messages = [
        {"role": "system", "content": full_system_prompt},
        {"role": "user", "content": prompt}
    ]
    
    # Define available tools for GPT-4o
    tools = [
        {
            "type": "function",
            "function": {
                "name": "search_market_trends",
                "description": "Deeply analyze market trends, competition, and pricing for any niche.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "topic": {"type": "string", "description": "The niche or product to research."}
                    },
                    "required": ["topic"]
                }
            }
        },
        {
            "type": "function",
            "function": {
                "name": "create_stripe_product_with_price",
                "description": "Create a real Product and Price in Stripe.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "name": {"type": "string"},
                        "description": {"type": "string"},
                        "unit_amount_cents": {"type": "integer"},
                        "currency": {"type": "string"}
                    },
                    "required": ["name", "description", "unit_amount_cents"]
                }
            }
        },
        {
            "type": "function",
            "function": {
                "name": "generate_image",
                "description": "Generate a branded image using free ZeroGPU fallbacks.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "prompt": {"type": "string", "description": "Description of the image to generate."}
                    },
                    "required": ["prompt"]
                }
            }
        },
        {
            "type": "function",
            "function": {
                "name": "deploy_to_sagemaker",
                "description": "Deploy a Hugging Face model to AWS SageMaker.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "model_id": {"type": "string"},
                        "instance_type": {"type": "string"},
                        "region": {"type": "string"}
                    },
                    "required": ["model_id"]
                }
            }
        },
        {
            "type": "function",
            "function": {
                "name": "call_bedrock_intelligence",
                "description": "Query AWS Bedrock for advanced reasoning.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "prompt": {"type": "string"},
                        "model_id": {"type": "string"}
                    },
                    "required": ["prompt"]
                }
            }
        }
    ] if use_tools else None

    try:
        # GPT-4o Upgrade
        completion_args = {
            "model": "gpt-4o",
            "messages": messages
        }
        if tools:
            completion_args["tools"] = tools
            completion_args["tool_choice"] = "auto"

        response = client.chat.completions.create(**completion_args)
        
        response_message = response.choices[0].message
        tool_calls = response_message.tool_calls
        
        if tool_calls:
            messages.append(response_message)
            for tool_call in tool_calls:
                function_name = tool_call.function.name
                args = json.loads(tool_call.function.arguments)
                
                if function_name == "search_market_trends":
                    result = search_market_trends_internal(args["topic"])
                elif function_name == "generate_image":
                    result = generate_image_internal(args["prompt"])
                elif function_name == "deploy_to_sagemaker":
                    result = deploy_to_sagemaker(args["model_id"], args.get("instance_type", "ml.g5.xlarge"), args.get("region", "ap-southeast-2"))
                elif function_name == "call_bedrock_intelligence":
                    result = call_bedrock_intelligence(args["prompt"], args.get("model_id", "anthropic.claude-3-5-sonnet-20240620-v1:0"))
                else:
                    result = "Tool not implemented."
                
                messages.append({
                    "tool_call_id": tool_call.id,
                    "role": "tool",
                    "name": function_name,
                    "content": result,
                })
            
            # Get final response after tools
            second_response = client.chat.completions.create(
                model="gpt-4o",
                messages=messages,
            )
            return second_response.choices[0].message.content
            
        return response_message.content

    except Exception as e:
        # IQ-300 Free Fallback (Llama 3.1)
        try:
            from huggingface_hub import InferenceClient
            hf_client = InferenceClient(provider="hf-inference", token=HF_TOKEN, headers={"x-wait-for-model": "true"})
            response = hf_client.chat_completion(model="meta-llama/Meta-Llama-3.1-8B-Instruct", messages=messages, max_tokens=1500)
            return response.choices[0].message.content
        except Exception as hf_e:
            return f"Intelligence Error: {str(e)}"

# --- INTERNAL TOOLS (Actual Logic) ---

def search_market_trends_internal(topic: str) -> str:
    prompt = f"Conduct a professional market research analysis for the niche: '{topic}'. Suggest pricing and identify competitors."
    return llm_worker(prompt, use_tools=False, persona="author")

def generate_image_internal(prompt: str) -> str:
    from gradio_client import Client
    import shutil
    
    business_name = os.environ.get("BUSINESS_NAME", "Fair Dinkum Publishing")
    owner = os.environ.get("BUSINESS_OWNER", "BRETT SJOBERG")
    abn = os.environ.get("BUSINESS_ABN", "63 590 716 023")
    
    brand_context = f"Professional branded asset for {business_name}. Owner: {owner}, ABN: {abn}. Style: Modern, high-intelligence. "
    full_prompt = brand_context + prompt
    
    try:
        client = Client("mrfakename/Z-Image-Turbo", token=HF_TOKEN)
        result = client.predict(prompt=full_prompt, height=1024, width=1024, num_inference_steps=9, seed=42, randomize_seed=True, api_name="/generate_image")
        temp_path = result[0] if isinstance(result, (list, tuple)) else result
        model_used = "Z-Image-Turbo"
    except Exception:
        client = Client("black-forest-labs/FLUX.1-schnell", token=HF_TOKEN)
        result = client.predict(prompt=full_prompt, seed=0, randomize_seed=True, width=1024, height=1024, num_inference_steps=4, api_name="/infer")
        temp_path = result[0] if isinstance(result, (list, tuple)) else result
        model_used = "FLUX.1-schnell"
    
    os.makedirs("exports/images", exist_ok=True)
    final_path = f"exports/images/{abs(hash(prompt))}.png"
    shutil.copy(temp_path, final_path)
    return f"Branded Image Generated using {model_used}: {final_path}."

def create_stripe_product_with_price_internal(name: str, description: str, unit_amount_cents: int, currency: str = "aud") -> str:
    try:
        product = stripe.Product.create(name=name, description=description)
        price = stripe.Price.create(product=product.id, unit_amount=unit_amount_cents, currency=currency)
        return f"Product Created: {name} (ID: {product.id}). Price Created (ID: {price.id})."
    except Exception as e:
        return f"Stripe Error: {str(e)}"

def create_stripe_payment_link_internal(price_id: str) -> str:
    try:
        payment_link = stripe.PaymentLink.create(line_items=[{"price": price_id, "quantity": 1}])
        return f"Payment Link Created: {payment_link.url}"
    except Exception as e:
        return f"Stripe Error: {str(e)}"

# --- EXPOSED MCP TOOLS ---

@mcp.tool()
def create_stripe_payment_link(price_id: str) -> str:
    """Create a durable, permanent Stripe Payment Link for a given Price ID."""
    return create_stripe_payment_link_internal(price_id)

@mcp.tool()
def search_market_trends(topic: str) -> str:
    """Deeply analyze market trends, competition, and pricing."""
    return search_market_trends_internal(topic)

@mcp.tool()
def create_stripe_product_with_price(name: str, description: str, unit_amount_cents: int, currency: str = "aud") -> str:
    """Create a real Product and Price in Stripe."""
    return create_stripe_product_with_price_internal(name, description, unit_amount_cents, currency)

@mcp.tool()
def launch_ebook_business(title: str, author: str, topic: str) -> str:
    """Automated sequence for ebook business generation and Hub registration."""
    chapters = [{"title": "Introduction", "content": f"A guide to {topic}."}]
    base_name = title.lower().replace(" ", "_").replace("'", "")
    epub_path, pdf_path = create_ebook_files(title, author, chapters, base_name=base_name)
    
    project_data = {
        "title": title,
        "author": author,
        "topic": topic,
        "files": {"epub": epub_path, "pdf": pdf_path}
    }
    
    filename = f"launch_{base_name}.json"
    os.makedirs("projects", exist_ok=True)
    with open(os.path.join("projects", filename), "w") as f:
        json.dump(project_data, f, indent=2)
    
    save_to_databank(filename, project_data, folder="projects")
    return f"Business Launched: '{title}' created and registered. Refresh Hub to view."

@mcp.tool()
def generate_image(prompt: str) -> str:
    """Generate a branded cover or marketing asset."""
    return generate_image_internal(prompt)

@mcp.tool()
def create_gcs_bucket(bucket_name: str, project_id: str = "automatedworkspaceworkflows", location: str = "us-central1") -> str:
    """Create a new GCS bucket for data storage."""
    import subprocess
    cmd = ["gcloud", "storage", "buckets", "create", f"gs://{bucket_name}", "--project", project_id, "--location", location]
    result = subprocess.run(cmd, capture_output=True, text=True)
    return f"GCS Result: {result.stdout or result.stderr}"

@mcp.tool()
def databank_search(query: str) -> str:
    """IQ-300 Memory: Search the Fair Dinkum Databank."""
    abn = os.environ.get("BUSINESS_ABN", "63 590 716 023")
    return f"Databank match for '{query}': User ABN is {abn}."

# --- AGENT LOGIC (Autonomous Router) ---

def aussie_router(user_input, history):
    context = databank_search(user_input)
    system_instr = load_from_databank("router_instructions.md") or "You are the Aussie Domain Router."
    examples = load_examples("router")
    
    full_system_prompt = f"{system_instr}\n{examples}\n\n### CONTEXT FROM DATABANK:\n{context}\n\nAct autonomously. Use tools directly."
    
    return llm_worker(user_input, system_prompt=full_system_prompt)

# --- GRADIO UI ---

def get_all_projects():
    projects = {}
    if os.path.exists("projects"):
        for filename in os.listdir("projects"):
            if filename.endswith(".json"):
                try:
                    with open(os.path.join("projects", filename), "r") as f:
                        data = json.load(f)
                        projects[data["title"]] = data
                except Exception:
                    continue
    return projects

all_projects = get_all_projects()

with gr.Blocks(title="Aussie Agent Hub") as demo:
    gr.Markdown("# 🐨 Aussie MCP Server Agent Hub (37-Agent Workforce)")
    
    with gr.Row():
        with gr.Column(scale=1):
            gr.Markdown("### πŸš€ Venture Showcase")
            project_selector = gr.Dropdown(
                choices=["Main Hub"] + list(all_projects.keys()),
                value="Main Hub",
                label="Active Venture"
            )
            project_info = gr.Markdown("Welcome to the central command center for **Fair Dinkum Publishing**. Orchestrate your 37-agent AI workforce below.")
            epub_dl = gr.File(label="Download EPUB", visible=False)
            pdf_dl = gr.File(label="Download PDF", visible=False)
            buy_link = gr.Markdown(visible=False)

    with gr.Tab("Chat with Hub"):
        chatbot = gr.Chatbot()
        msg = gr.Textbox(placeholder="Ask your Aussie Agent anything...")
        clear = gr.Button("Clear")

    def update_project_ui(choice):
        if choice == "Main Hub":
            return ["Welcome to the central command center.", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)]
        proj = all_projects.get(choice)
        if not proj: return ["Project not found.", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)]
        
        info = f"Viewing interactive hub for **{proj['title']}**."
        epub_visible = "epub" in proj.get("files", {}) and os.path.exists(proj["files"]["epub"])
        pdf_visible = "pdf" in proj.get("files", {}) and os.path.exists(proj["files"]["pdf"])
        
        # Use permanent Payment Link if available, otherwise fallback to Price ID session
        buy_url = proj.get("payment_link") or (f"https://buy.stripe.com/{proj['price_id']}" if "price_id" in proj else None)
        buy_visible = buy_url is not None
        
        return [
            info,
            gr.update(value=proj["files"].get("epub") if epub_visible else None, visible=epub_visible),
            gr.update(value=proj["files"].get("pdf") if pdf_visible else None, visible=pdf_visible),
            gr.update(value=f"**Special Offer:** [Buy Now]({buy_url})" if buy_visible else "", visible=buy_visible)
        ]

    project_selector.change(update_project_ui, project_selector, [project_info, epub_dl, pdf_dl, buy_link])

    def user(user_message, history, current_venture):
        context_msg = f"[Context: {current_venture}] {user_message}" if current_venture != "Main Hub" else user_message
        return "", history + [[user_message, None]], context_msg

    def bot(history, context_msg):
        bot_message = aussie_router(context_msg, history[:-1])
        history[-1][1] = bot_message
        return history

    msg.submit(user, [msg, chatbot, project_selector], [msg, chatbot, msg], queue=False).then(bot, [chatbot, msg], chatbot)
    clear.click(lambda: None, None, chatbot, queue=False)

if __name__ == "__main__":
    demo.launch(server_name="0.0.0.0", server_port=7860)