import flask from flask import request, jsonify, send_from_directory from flask_cors import CORS from transformers import AutoTokenizer, AutoModelForCausalLM import torch import json import re import os import datetime app = flask.Flask(__name__, static_folder="static") CORS(app) # ── Model Setup ──────────────────────────────────────────────────────────────── MODEL_ID = "Qwen/Qwen2.5-1.5B-Instruct" SEO_SYSTEM_PROMPT = """You are "Juskeo AI", an expert SEO/GEO analyst. Analyze the input content and return STRICTLY a raw JSON object (no markdown, no backticks) with these exact keys: { "seo_title": "60-char optimized title", "meta_description": "155-char meta description", "seo_keywords": ["keyword1", "keyword2", ...], "focus_keyword": "primary keyword", "schema_type": "Article|BlogPosting|FAQPage", "faqs": [{"question": "...", "answer": "..."}], "og_title": "Open Graph title", "og_description": "Open Graph description", "canonical_hint": "suggested URL slug", "readability_score": "Good|Average|Poor", "word_count_estimate": 0, "geo_signals": ["city/region keywords if any"], "lsi_keywords": ["semantic keyword1", "semantic keyword2"] }""" BLOG_SYSTEM_PROMPT = """You are "Juskeo AI Content Writer". Write a highly detailed, comprehensive, long-form blog post. Include: introduction, multiple H2/H3 sections, historical context, technical details, future impacts, FAQs, conclusion. Use markdown formatting with ##, ###, **bold**, - lists. Make it rich, 1500+ words.""" print(f"🔄 Loading {MODEL_ID}...") tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True, use_fast=True) if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token raw_model = AutoModelForCausalLM.from_pretrained( MODEL_ID, torch_dtype=torch.float32, trust_remote_code=True, low_cpu_mem_usage=True ) print("⚡ Applying 8-bit CPU quantization...") model = torch.quantization.quantize_dynamic( raw_model, {torch.nn.Linear}, dtype=torch.qint8 ) device = torch.device("cpu") model.eval() print("✅ Juskeo AI Engine Ready!") # ── Helper: Run LLM ──────────────────────────────────────────────────────────── def run_llm(system_prompt, user_content, max_new_tokens=400, temperature=0.0, do_sample=False): chat_history = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_content} ] formatted = tokenizer.apply_chat_template(chat_history, tokenize=False, add_generation_prompt=True) inputs = tokenizer(formatted, return_tensors="pt", padding=True, truncation=True, max_length=2048) inputs = {k: v.to(device) for k, v in inputs.items()} with torch.inference_mode(): output = model.generate( **inputs, max_new_tokens=max_new_tokens, do_sample=do_sample, temperature=temperature if do_sample else 1.0, top_p=0.9 if do_sample else 1.0, pad_token_id=tokenizer.pad_token_id, eos_token_id=tokenizer.eos_token_id ) generated_tokens = output[0][inputs['input_ids'].shape[1]:] return tokenizer.decode(generated_tokens, skip_special_tokens=True).strip() def clean_json(raw): """Strip markdown fences and extract JSON.""" cleaned = re.sub(r"```(?:json)?", "", raw).replace("```", "").strip() # Try to find JSON object match = re.search(r'\{.*\}', cleaned, re.DOTALL) return match.group(0) if match else cleaned def build_schema_jsonld(seo_data, article_text): """Build JSON-LD schema markup from SEO data.""" schema_type = seo_data.get("schema_type", "Article") faqs = seo_data.get("faqs", []) schemas = [] # Article/BlogPosting schema article_schema = { "@context": "https://schema.org", "@type": schema_type, "headline": seo_data.get("seo_title", ""), "description": seo_data.get("meta_description", ""), "keywords": ", ".join(seo_data.get("seo_keywords", [])), "datePublished": datetime.datetime.utcnow().isoformat() + "Z", "author": {"@type": "Organization", "name": "Juskeo AI"} } schemas.append(article_schema) # FAQ schema if FAQs exist if faqs: faq_schema = { "@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [ { "@type": "Question", "name": faq.get("question", ""), "acceptedAnswer": { "@type": "Answer", "text": faq.get("answer", "") } } for faq in faqs ] } schemas.append(faq_schema) return schemas def generate_full_html(seo_data, blog_content, article_title): """Generate a complete, SEO-ready HTML page.""" title = seo_data.get("seo_title", article_title) meta_desc = seo_data.get("meta_description", "") keywords = ", ".join(seo_data.get("seo_keywords", [])) og_title = seo_data.get("og_title", title) og_desc = seo_data.get("og_description", meta_desc) canonical_slug = seo_data.get("canonical_hint", "article") schemas = build_schema_jsonld(seo_data, blog_content) schema_scripts = "\n".join([ f'' for s in schemas ]) # Convert markdown to basic HTML html_body = blog_content html_body = re.sub(r'^### (.+)$', r'
', html_body) html_body = f"
{html_body}
" faq_html = "" for faq in seo_data.get("faqs", []): faq_html += f"""{faq.get('answer','')}