| """ |
| RUBRA v8 — Ultimate Agentic Intelligence |
| NEW: Multi-model Vision (Groq Vision + Qwen2.5-VL + OCR.space) |
| NCTB 2026 Full Curriculum Knowledge |
| UI/UX Pro Max Coding Engine (67 styles, 161 rules) |
| Advanced Free Models upgraded |
| """ |
| import os,sys,re,json,time,uuid,math,sqlite3,hashlib,asyncio,logging |
| import base64,mimetypes,io,threading |
| import xml.etree.ElementTree as ET |
| from pathlib import Path |
| from typing import Optional,AsyncIterator |
| from datetime import datetime,timezone |
| import aiohttp,requests as _req |
| from fastapi import FastAPI,UploadFile,File,Form,WebSocket,WebSocketDisconnect |
| from fastapi.middleware.cors import CORSMiddleware |
| from fastapi.responses import StreamingResponse,JSONResponse |
| from pydantic import BaseModel |
| from urllib.parse import urljoin, urlparse, quote_plus |
| from html.parser import HTMLParser |
| import urllib.robotparser |
|
|
| HERE = Path(__file__).resolve().parent |
| os.chdir(str(HERE)) |
|
|
| |
| |
| |
| ZAI_KEY = os.getenv("ZAI_API_KEY", "b4a30453455d4c5fa63d63ce32b71506.k1s8vXrPLKnr3m5l") |
| GROQ_KEY = os.getenv("GROQ_API_KEY", "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") |
| OR_KEY = os.getenv("OPENROUTER_KEY", "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") |
| OCR_KEY = os.getenv("OCR_SPACE_KEY", "helloworld") |
| GEMINI_KEY = os.getenv("GEMINI_API_KEY", "AIzaSyAu4k8XvVMVse2oV47u2QFLOJcqOxUui-c") |
| CEREBRAS_KEY = os.getenv("CEREBRAS_API_KEY", "csk-hrrmp8cp4j94kdhdxcdjp8v3ctmvc2tc3et2pyyywdr9xjjw") |
|
|
| ZAI_CHAT = "https://api.z.ai/api/paas/v4/chat/completions" |
| ZAI_CODE = "https://api.z.ai/api/coding/paas/v4/chat/completions" |
| GROQ_URL = "https://api.groq.com/openai/v1/chat/completions" |
| OR_URL = "https://openrouter.ai/api/v1/chat/completions" |
| GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions" |
| CEREBRAS_URL = "https://api.cerebras.ai/v1/chat/completions" |
|
|
| DB_PATH = HERE / "rubra.db" |
| UPLOAD_DIR = HERE / "uploads" |
| UPLOAD_DIR.mkdir(exist_ok=True) |
|
|
| import shutil |
|
|
| |
| SKILLS_DIR = HERE / "skills" |
| SKILLS_DIR.mkdir(exist_ok=True) |
|
|
| |
| SESSION_DIR = HERE / "sessions" |
| SESSION_DIR.mkdir(exist_ok=True) |
|
|
| def session_path(sid: str) -> Path: |
| return SESSION_DIR / f"{sid}.json" |
|
|
| def session_load(sid: str) -> dict: |
| p = session_path(sid) |
| if p.exists(): |
| try: return json.loads(p.read_text()) |
| except: pass |
| return {"session_id":sid,"created_at":time.time(),"user_intent_history":[], |
| "preferred_lang":"en","coding_style":"clean","topic_memory":{}, |
| "skill_calls":[],"thought_chain":[]} |
|
|
| def session_save(sid: str, data: dict): |
| try: session_path(sid).write_text(json.dumps(data,ensure_ascii=False,indent=2)) |
| except Exception as e: log.warning(f"Session save: {e}") |
|
|
| def session_update(sid: str, key: str, value): |
| data = session_load(sid) |
| if key in ("user_intent_history","skill_calls","thought_chain"): |
| lst = data.get(key,[]); lst.append(value); data[key] = lst[-50:] |
| else: data[key] = value |
| session_save(sid, data) |
|
|
| logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") |
| log = logging.getLogger("rubra") |
|
|
| |
| |
| |
| |
|
|
| import csv, io, math, re |
| from typing import Optional |
|
|
| |
| |
|
|
| |
| _STYLES_CSV = """id,name,keywords,best_for,performance,accessibility,effects,avoid |
| 1,Minimalism & Swiss Style,"minimal,clean,simple,whitespace,grid,typography",Enterprise apps / dashboards / documentation,Excellent,WCAG AA,"Strict grid, typography-first, monochrome","Excessive decoration, gradients" |
| 2,Neumorphism,"soft,shadow,emboss,depth,subtle,tactile",Health/wellness / meditation / premium tools,Good,Moderate,"Soft box-shadows, monochromatic surfaces","Dark backgrounds, low contrast" |
| 3,Glassmorphism,"glass,frosted,blur,transparent,modern,saas",Modern SaaS / fintech dashboards / AI products,Good,WCAG AA,"backdrop-filter:blur, rgba backgrounds, subtle borders","Pure white bg (needs dark/image bg)" |
| 4,Brutalism,"raw,bold,borders,clash,chaotic,contrast",Design portfolios / artistic projects / anti-brand,Excellent,Moderate,"Thick borders, unexpected layouts, monospace","Subtlety, drop shadows" |
| 5,3D & Hyperrealism,"3d,realistic,immersive,product,depth",Gaming / product showcase / immersive experiences,Heavy,Moderate,"Three.js / CSS 3D transforms, ambient lighting","Flat icons, 2D-only approach" |
| 6,Vibrant & Block-based,"vibrant,block,bold,color,punch,energy",Startups / creative agencies / gaming,Good,WCAG AA,"Bold color blocks, high contrast, geometric","Muted palettes, soft shadows" |
| 7,Dark Mode OLED,"dark,black,oled,night,low-light,code",Night apps / coding platforms / media,Excellent,WCAG AA,"#000000 bg, minimal battery (OLED), subtle glows","Pure white text (use #e2e8f0)" |
| 8,Accessible & Ethical,"accessible,wcag,inclusive,government,high-contrast",Government / healthcare / education,Excellent,WCAG AAA,"4.5:1+ contrast, skip links, focus rings","Decorative-only elements" |
| 9,Claymorphism,"clay,3d-soft,puffy,inflated,cartoon,playful",Educational / children's / SaaS onboarding,Good,Moderate,"Inflated 3D shapes, pastel fills, thick outlines","Photorealism, dark themes" |
| 10,Aurora UI,"aurora,gradient,mesh,glow,animated,modern",Modern SaaS / creative agencies / AI,Good,WCAG AA,"Gradient mesh bg, animated aurora effects","Static solid backgrounds" |
| 11,Retro-Futurism,"retro,80s,synthwave,neon,nostalgia,sci-fi",Gaming / entertainment / music platforms,Good,Moderate,"Scanlines, neon glows, grid lines, CRT effects","Flat modern icons" |
| 12,Flat Design,"flat,simple,2d,clean,icon,material-lite",Web apps / mobile apps / startup MVPs,Excellent,WCAG AA,"No shadows, bold icons, solid fills","Gradients, textures, 3D" |
| 13,Skeuomorphism,"realistic,texture,leather,wood,physical,tangible",Legacy apps / premium audio / gaming,Heavy,Moderate,"Texture fills, realistic shadows, material surfaces","Overuse (looks dated)" |
| 14,Liquid Glass,"liquid,fluid,premium,shine,depth,apple",Premium SaaS / high-end e-commerce,Good,WCAG AA,"Multi-layer blur, prismatic effects, specular highlights","Low-end devices (perf)" |
| 15,Motion-Driven,"motion,animation,scroll,reveal,gsap,storytelling",Portfolio sites / storytelling / brand launches,Heavy,Moderate,"GSAP/Framer scroll triggers, parallax, SVG morphing","prefers-reduced-motion neglect" |
| 16,Micro-interactions,"micro,feedback,haptic,touch,response,mobile",Mobile apps / touchscreen UIs / forms,Good,WCAG AA,"Bounce, spring physics, press states, ripple","Desktop-only hover states" |
| 17,Inclusive Design,"inclusive,a11y,screen-reader,wcag-aaa,empathy",Public services / education / healthcare,Excellent,WCAG AAA,"High contrast modes, large targets 44px+, aria labels","Icon-only buttons" |
| 18,Zero Interface,"voice,ai,ambient,invisible,conversational,minimal",Voice assistants / AI platforms / smart home,Excellent,High,"Minimal chrome, audio feedback, ambient display","Visual overload" |
| 19,Soft UI Evolution,"soft,modern,rounded,neutral,enterprise,saas",Modern enterprise / SaaS / productivity,Excellent,WCAG AA,"Subtle elevation, rounded-2xl, neutral palette","Sharp edges, harsh shadows" |
| 20,Neubrutalism,"neubrutalism,thick-border,offset-shadow,flat,genz,figma",Gen Z brands / startups / Figma-style,Excellent,WCAG AA,"2-4px solid borders, offset box-shadow, flat fills","Subtle or rounded styles" |
| 21,Bento Box Grid,"bento,grid,card,dashboard,asymmetric,modular",Dashboards / product pages / portfolios,Excellent,WCAG AA,"Asymmetric grids, varying card sizes, hover reveals","Single-column layouts" |
| 22,Y2K Aesthetic,"y2k,chrome,bubble,millennium,iridescent,2000s",Fashion / music / Gen Z,Good,Moderate,"Chrome text, iridescent fills, bubble UI, star shapes","Minimalism" |
| 23,Cyberpunk UI,"cyber,neon,dark,grid,glitch,hacker,matrix",Gaming / tech products / crypto apps,Good,Moderate,"Neon #00fff5/#ff00ff, grid overlays, glitch animations","Pastel colors" |
| 24,Organic Biophilic,"organic,nature,leaf,earth,green,wellness",Wellness apps / sustainability brands,Good,WCAG AA,"Leaf shapes, earth tones, organic curves, textures","Geometric rigidity" |
| 25,AI-Native UI,"ai,chat,copilot,streaming,dark,assistant",AI products / chatbots / copilots,Excellent,WCAG AA,"Dark base #0a0a0a, typing indicators, token streaming","Busy backgrounds" |
| 26,Memphis Design,"memphis,geometric,pattern,80s,playful,retro",Creative agencies / music / youth brands,Good,Moderate,"Bold geometric shapes, primary colors, pattern fills","Subtlety" |
| 27,Vaporwave,"vaporwave,pink,purple,retrowave,aesthetic,sunset",Music platforms / gaming / portfolios,Good,Moderate,"#ff6ec7/#b967ff palette, grid floors, retro 3D","Corporate contexts" |
| 28,Dimensional Layering,"layers,depth,z-index,card,floating,elevation",Dashboards / card layouts / modals,Good,WCAG AA,"Multiple elevation levels, floating panels, blur depth","Flat single-layer" |
| 29,Exaggerated Minimalism,"extreme,whitespace,large-type,fashion,editorial",Fashion / architecture / luxury,Excellent,Moderate,"Massive whitespace, oversized typography, restraint","Crowded layouts" |
| 30,Kinetic Typography,"kinetic,text-animation,headline,gsap,scroll",Hero sections / marketing sites / brand,Heavy,Moderate,"Text reveals, character-by-character, scroll-linked","Body text animations" |
| 31,Parallax Storytelling,"parallax,scroll,depth,story,reveal,brand",Brand storytelling / product launches,Heavy,Moderate,"Multi-layer parallax, scroll-triggered reveals","prefers-reduced-motion neglect" |
| 32,Swiss Modernism 2.0,"swiss,grid,helvetica,corporate,architecture,editorial",Corporate / architecture / editorial,Excellent,WCAG AA,"Strict typographic grid, Helvetica/Neue, asymmetry","Decorative elements" |
| 33,HUD Sci-Fi FUI,"hud,fui,hologram,scan,radar,space,tech",Sci-fi games / space tech / cybersecurity,Good,Moderate,"Radar animations, scan lines, holographic circles","Consumer apps" |
| 34,Pixel Art,"pixel,8bit,retro,game,sprite,indie",Indie games / retro tools / creative,Good,Moderate,"Pixel fonts, sprite animations, grid-snapped","High-res photography" |
| 35,Spatial UI VisionOS,"spatial,visionos,ar,vr,depth,frosted,apple",Spatial computing / VR/AR apps,Good,Moderate,"Multi-plane layers, frosted glass panels, depth","2D flat interfaces" |
| 36,E-Ink Paper,"e-ink,paper,reading,monochrome,newspaper,minimal",Reading apps / digital newspapers,Excellent,WCAG AA,"High contrast B&W, minimal animation, serif type","Color fills, animations" |
| 37,Gen Z Chaos Maximalism,"maximal,chaotic,clutter,intense,doomscroll,loud",Gen Z lifestyle / music artists,Good,Low,"Overlapping elements, mixed fonts, noise textures","Order and alignment" |
| 38,Biomimetic Organic,"biomimetic,biotech,science,organic-2,cell,nature-tech",Sustainability tech / biotech / health,Good,Moderate,"Cell-like shapes, bioluminescent glows, fluid motion","Rigid geometric" |
| 39,Anti-Polish Raw,"raw,unfinished,authentic,sketch,handmade,lo-fi",Creative portfolios / artist sites,Excellent,Moderate,"Sketch lines, imperfect shapes, handwritten type","Polished corporate" |
| 40,Tactile Deformable,"tactile,press,deform,elastic,spring,physical",Modern mobile / playful brands,Good,WCAG AA,"Spring physics on press, elastic deformation","Desktop-only designs" |
| 41,Nature Distilled,"nature,botanical,minimal-organic,calm,earth,leaf",Wellness brands / sustainable products,Excellent,WCAG AA,"Botanical illustrations, earth tones, organic type","Neon or synthetic colors" |
| 42,Interactive Cursor,"cursor,custom,trail,magnetic,hover-effect,creative",Creative portfolios / interactive agencies,Good,Moderate,"Magnetic hover, cursor trails, custom SVG cursors","Mobile (no cursor)" |
| 43,Voice-First Multimodal,"voice,multimodal,speech,waveform,accessibility-first",Voice assistants / accessibility apps,Excellent,WCAG AAA,"Waveform animations, voice indicators, minimal text","Dense UI" |
| 44,3D Product Preview,"3d-product,ecommerce-3d,rotate,webgl,try-on,ar",E-commerce / furniture / fashion,Heavy,Moderate,"Three.js product viewer, AR try-on, zoom controls","Non-product pages" |
| 45,Gradient Mesh Aurora,"mesh,aurora-evolved,gradient-mesh,background,hero",Hero sections / backgrounds / creative,Good,WCAG AA,"CSS mesh gradients, animated color stops","Overuse on content areas" |
| 46,Editorial Grid Magazine,"editorial,magazine,grid-break,journalism,article",News sites / blogs / magazines,Excellent,WCAG AA,"Pull quotes, column grids, drop caps, image bleeds","Dashboard layouts" |
| 47,Chromatic Aberration,"chromatic,rgb-split,glitch-color,psychedelic,lens",Music platforms / gaming / experimental,Good,Moderate,"RGB channel offset, lens distortion, color split","Professional contexts" |
| 48,Vintage Analog Retro Film,"vintage,film,grain,analog,vinyl,photography-retro",Photography / music / vinyl brands,Good,Moderate,"Film grain overlay, sepia/fade, retro color grading","Modern tech products" |
| 49,Hero-Centric Design,"hero,landing,fullscreen,above-fold,cta,conversion",Products with strong visual identity,Excellent,WCAG AA,"Full-viewport hero, single CTA, visual hierarchy","Feature-heavy first screen" |
| 50,Conversion-Optimized,"conversion,cro,sales,lead-gen,funnel,cta-heavy",Lead generation / sales pages,Excellent,WCAG AA,"CTA repetition, urgency signals, social proof inline","Distracting animations" |
| 51,Feature-Rich Showcase,"features,saas-landing,comparison,benefits,icons",SaaS / complex products,Good,WCAG AA,"Feature grid, icon + text, comparison tables","Wall of text" |
| 52,Minimal & Direct,"minimal-landing,simple,fast,single-cta,startup",Simple products / apps / MVPs,Excellent,WCAG AA,"One message, one CTA, fast load","Feature overload" |
| 53,Social Proof Focused,"testimonial,review,trust,rating,social-proof,b2c",Services / B2C products,Excellent,WCAG AA,"Star ratings, photo testimonials, logos bar","Generic stock photos" |
| 54,Interactive Product Demo,"demo,interactive,saas-demo,try,product-tour",Software / tools,Good,WCAG AA,"Inline demos, hotspot tours, video embeds","Autoplay audio" |
| 55,Trust & Authority,"b2b,enterprise,authority,case-study,white-paper",B2B / enterprise / consulting,Excellent,WCAG AA,"Case studies, client logos, certifications","Casual or playful tone" |
| 56,Storytelling-Driven Landing,"story,narrative,scroll-story,brand-story,emotional",Brands / agencies / nonprofits,Good,WCAG AA,"Chapter-based scroll, emotional photography","Hard selling" |
| 57,Data-Dense Dashboard,"data-dense,bi,analytics,tables,metrics,kpi",Complex data analysis,Good,WCAG AA,"Compact rows, sparklines, progressive disclosure","Large empty space" |
| 58,Heat Map Style,"heatmap,geographic,behavioral,density,choropleth",Geographic / behavior data,Good,Moderate,"Color gradient maps, zoom controls, legends","Non-data contexts" |
| 59,Executive Dashboard,"executive,c-suite,summary,kpi-only,high-level",C-suite summaries,Excellent,WCAG AA,"Large KPI numbers, trend arrows, minimal detail","Detailed raw data" |
| 60,Real-Time Monitoring,"realtime,ops,devops,alert,live-feed,websocket",Operations / DevOps / trading,Good,WCAG AA,"Live updates, alert badges, status indicators","Static screenshots" |
| 61,Drill-Down Analytics,"drilldown,explore,filter,pivot,tableau-style",Detailed data exploration,Good,WCAG AA,"Expandable rows, filter panels, breadcrumb nav","Flat single-level" |
| 62,Comparative Analysis,"compare,side-by-side,benchmark,before-after,vs",Side-by-side comparisons,Good,WCAG AA,"Split layouts, synchronized scroll, diff views","Single-view only" |
| 63,Predictive Analytics,"predictive,forecast,ml,trend,projection,ai-chart",Forecasting / ML insights,Good,WCAG AA,"Confidence bands, scenario toggles, trend lines","Historical-only views" |
| 64,User Behavior Analytics,"uba,session,funnel,cohort,product-analytics",UX research / product analytics,Good,WCAG AA,"Funnel charts, session replays, cohort grids","Generic BI styles" |
| 65,Financial Dashboard,"finance,accounting,p&l,revenue,cashflow,trading",Finance / accounting,Good,WCAG AA,"Candlestick charts, P&L tables, green/red coding","Casual color palette" |
| 66,Sales Intelligence,"sales,crm,pipeline,deals,revenue-forecast,salesforce",Sales teams / CRM,Good,WCAG AA,"Pipeline stages, win/loss, quota progress bars","Non-sales contexts" |
| 67,Ambient Calm UI,"calm,ambient,peaceful,breathe,zen,wellness-app",Meditation / sleep / mental health,Excellent,WCAG AA,"Slow animations, muted palette, breathing space","High-energy interactions" |
| """ |
|
|
| |
| _RULES_CSV = """id,product_type,keywords,pattern,style_priority,color_mood,typography_mood,key_effects,anti_patterns |
| 1,SaaS Platform,"saas,software,subscription,b2b,platform",Feature-Rich Showcase,Glassmorphism|Soft UI Evolution|Bento Box Grid,Blue-indigo neutral,"Inter/DM Sans, clean","Subtle hover elevations, smooth transitions","AI purple gradients on white" |
| 2,Micro SaaS,"micro-saas,indie,solo,niche,small-saas",Minimal & Direct,Minimalism|Flat Design|Neubrutalism,Focused monochrome,"Geist, modern mono","Single CTA, fast load","Feature overload" |
| 3,B2B Service,"b2b,enterprise,consulting,professional,corporate",Trust & Authority,Swiss Modernism 2.0|Soft UI Evolution,Navy-gray professional,"Neue Haas, IBM Plex","Case study cards, logo bars","Playful colors, emoji icons" |
| 4,Developer Tool IDE,"developer,devtool,ide,coding,terminal,cli",Dark Mode OLED|Minimal & Direct,Dark Mode OLED|Neubrutalism,Dark #0d1117 green accent,"JetBrains Mono, Geist Mono","Syntax highlighting, terminal feel","Colorful gradients" |
| 5,AI Chatbot Platform,"ai,chatbot,llm,assistant,copilot,gpt",AI-Native UI,AI-Native UI|Glassmorphism,Dark #0a0a0a rose/cyan accent,"Space Grotesk, JetBrains Mono","Token streaming, typing dots, glow","Busy backgrounds" |
| 6,Cybersecurity Platform,"security,cyber,soc,threat,firewall,pentest",HUD Sci-Fi FUI|Dark Mode OLED,HUD Sci-Fi FUI|Dark Mode OLED,Dark green matrix or dark blue,"Roboto Mono, IBM Plex Mono","Scan animations, alert pulses","Pastel friendly colors" |
| 7,No-Code Builder,"nocode,builder,drag-drop,visual,lowcode,website-builder",Bento Box Grid|Claymorphism,Bento Box Grid|Claymorphism|Flat Design,Vibrant primary,"Nunito, Poppins","Drag handles, live preview","Complex dark themes" |
| 8,Project Management,"project,task,kanban,todo,agile,trello,jira",Bento Box Grid|Soft UI Evolution,Bento Box Grid|Dimensional Layering,Neutral productive,"Inter, DM Sans","Drag kanban, progress bars","Information overload" |
| 9,Analytics Dashboard,"analytics,dashboard,metrics,kpi,reporting,bi",Data-Dense Dashboard,Bento Box Grid|Data-Dense Dashboard,Dark navy or white clean,"DM Mono, IBM Plex","Recharts/Chart.js, sparklines","Decorative-only charts" |
| 10,Fintech Crypto,"fintech,crypto,bitcoin,defi,wallet,blockchain,trading",Glassmorphism|Cyberpunk UI,Glassmorphism|Dark Mode OLED,Dark + green #00ff88 or cyan,"Roboto Mono, Space Grotesk","Live tickers, candlestick charts","Cute/playful elements" |
| 11,Banking App,"banking,bank,account,transfer,finance,money",Trust & Authority,Soft UI Evolution|Minimalism,Trustworthy navy/white,"Neue Haas Grotesk, Source Sans","Secure icons, progress steps","Gradients, neon" |
| 12,Insurance Platform,"insurance,policy,coverage,claim,premium",Trust & Authority,Minimalism|Swiss Modernism 2.0,Corporate blue-white,"IBM Plex, Source Serif","Step forms, coverage tables","Playful animations" |
| 13,Personal Finance Tracker,"budget,expense,saving,money-tracker,personal-finance",Soft UI Evolution|Bento Box Grid,Bento Box Grid|Soft UI Evolution,Calm green-white,"Nunito, Lato","Donut charts, goal bars","Complexity, dark themes" |
| 14,Invoice Billing Tool,"invoice,billing,payment,receipt,accounting",Minimalism|Flat Design,Minimalism|Flat Design,Clean white professional,"Inter, DM Sans","Clean tables, PDF export feel","Colorful decoration" |
| 15,Medical Clinic,"medical,clinic,doctor,health,patient,appointment",Accessible & Ethical,Accessible & Ethical|Inclusive Design,Trustworthy blue-white,"Source Sans, Open Sans","Step forms, time slots","Decorative elements" |
| 16,Pharmacy,"pharmacy,medicine,drug,prescription,rx",Accessible & Ethical,Minimalism|Accessible & Ethical,Clean green-white,"Source Sans, Helvetica","Search-first, dosage clarity","Dark themes" |
| 17,Mental Health App,"mental,therapy,anxiety,depression,mindfulness,calm",Ambient Calm UI|Organic Biophilic,Organic Biophilic|Neumorphism,Soft lavender-sage-white,"Nunito, Lato","Breathing animations, soft transitions","Harsh contrast, red alerts" |
| 18,Fitness Workout App,"fitness,workout,exercise,gym,training,sport",Vibrant & Block-based,Vibrant & Block-based|Dark Mode OLED,Dark + electric blue/orange,"Exo 2, Rajdhani","Progress rings, intensity meters","Soft pastels" |
| 19,Telemedicine,"telemedicine,telehealth,video-consult,remote-doctor",Accessible & Ethical,Accessible & Ethical|Minimalism,Medical blue-white,"Open Sans, Source Sans","Video UI, appointment flow","Complex animations" |
| 20,Dental Practice,"dental,dentist,teeth,orthodontic",Minimalism,Soft UI Evolution|Minimalism,Clean white-teal,"Source Sans, Lato","Before/after image slider","Dark heavy themes" |
| 21,General E-commerce,"ecommerce,shop,store,buy,cart,checkout",Social Proof-Focused,Social Proof-Focused|Hero-Centric Design,Brand-specific warm,"Poppins, Libre Baskerville","Product zoom, quick-add, reviews","Complex nav, buried CTA" |
| 22,Luxury E-commerce,"luxury,premium,high-end,fashion-store,couture",Exaggerated Minimalism,Exaggerated Minimalism|Editorial Grid,Monochrome + gold accent,"Cormorant Garamond, Montserrat","Large imagery, slow reveal","Cluttered discount badges" |
| 23,Marketplace P2P,"marketplace,p2p,listing,seller,peer-to-peer,airbnb-like",Flat Design|Bento Box Grid,Flat Design|Bento Box Grid,Neutral trust,"Inter, DM Sans","Filter sidebar, map view, ratings","Visual complexity" |
| 24,Subscription Box,"subscription,box,monthly,curated,unboxing",Hero-Centric Design|Social Proof,Social Proof-Focused,Warm unboxing palette,"Playfair Display, Lato","Unboxing animation, countdown","Cluttered product grid" |
| 25,Food Delivery,"food,delivery,restaurant-app,takeout,order,uber-eats",Motion-Driven|Hero-Centric,Vibrant & Block-based,Appetite-stimulating warm red/orange,"Nunito, Poppins","Food photos full-bleed, quick order","Muted colors" |
| 26,Beauty Spa,"beauty,spa,wellness,skincare,salon,massage",Soft UI Evolution|Organic Biophilic,Soft UI Evolution|Nature Distilled,Soft pink-sage-ivory,"Cormorant Garamond, Montserrat","Scroll reveals, before/after","Dark themes, neon" |
| 27,Restaurant,"restaurant,dining,cafe,food,menu,reservation",Hero-Centric Design,Hero-Centric Design|Editorial Grid,Warm appetite palette,"Playfair Display, Lato","Full-bleed food imagery, reservation CTA","Plain product grid" |
| 28,Hotel Resort,"hotel,resort,hospitality,accommodation,booking",Storytelling-Driven Landing,Storytelling-Driven|Exaggerated Minimalism,Luxury warm neutrals,"Cormorant Garamond, Raleway","Room galleries, availability calendar","Cluttered UI" |
| 29,Legal Services,"legal,law,attorney,lawyer,compliance",Trust & Authority,Trust & Authority|Swiss Modernism 2.0,Corporate navy-gold,"Georgia, Source Serif","Document download, consultation CTA","Playful colors" |
| 30,Home Services,"home,plumber,electrician,handyman,cleaning,hvac",Social Proof-Focused,Social Proof-Focused|Flat Design,Trust blue-white-orange,"Open Sans, Nunito","Service cards, zip-code entry","Overly corporate" |
| 31,Booking Appointment,"booking,appointment,schedule,calendar,reserve",Flat Design|Bento Box Grid,Flat Design|Soft UI Evolution,Clean actionable,"Inter, DM Sans","Calendar picker, time slots, confirm flow","Complex multi-step" |
| 32,Travel Agency,"travel,trip,vacation,flights,tourism,destination",Parallax Storytelling,Parallax Storytelling|Hero-Centric,Vivid destination-inspired,"Raleway, Open Sans","Destination cards, map, booking CTA","Text-heavy" |
| 33,Real Estate,"realestate,property,homes,listings,mortgage",Social Proof-Focused|Interactive Demo,Social Proof-Focused|Bento Box Grid,Trustworthy neutral,"Libre Baskerville, DM Sans","Map view, photo galleries, mortgage calc","Cluttered listings" |
| 34,Education Platform,"education,elearning,course,lms,online-learning",Claymorphism|Flat Design,Claymorphism|Flat Design,Bright primary friendly,"Nunito, Open Sans","Progress bars, quiz cards, certificate","Dark themes for study" |
| 35,K12 School,"school,k12,elementary,high-school,student",Claymorphism,Claymorphism|Flat Design,Bright primary,"Nunito, Quicksand","Fun animations, achievement badges","Adult-corporate" |
| 36,University College,"university,college,campus,academic",Swiss Modernism 2.0|Trust,Trust & Authority|Minimalism,Institutional navy/crimson,"Merriweather, Source Sans","Event calendar, department nav","Overly playful" |
| 37,Online Tutoring,"tutor,tutoring,private-lesson,homework-help",Claymorphism|Soft UI Evolution,Claymorphism|Soft UI Evolution,Warm encouraging,"Nunito, Poppins","Session booking, whiteboard feel","Intimidating academic" |
| 38,Language Learning,"language,translation,vocab,flashcard,duolingo-like",Gamification style,Claymorphism|Vibrant,Bright gamified,"Nunito, Fredoka","Streak counters, XP bars, celebration","Dull monochrome" |
| 39,Creative Portfolio,"portfolio,designer,artist,creative,showcase",Brutalism|Exaggerated Minimalism,Brutalism|Motion-Driven,Personal brand-specific,"Variable fonts, Clash Display","Strong grid, project hover reveals","Generic templates" |
| 40,Photography Portfolio,"photography,photographer,photo-portfolio",Exaggerated Minimalism|Editorial Grid,Exaggerated Minimalism,Monochrome or muted,"Cormorant Garamond, Futura","Masonry grid, lightbox, lazy-load","Text-heavy, colored UI" |
| 41,Creative Agency,"agency,studio,creative-studio,production",Motion-Driven|Brutalism,Motion-Driven|Neubrutalism,High contrast black/white + 1 accent,"Clash Display, Satoshi","GSAP text reveals, cursor effects","Generic Bootstrap" |
| 42,Graphic Design Tool,"design,figma-like,vector,illustration,canvas",Flat Design|Dark Mode,Dark Mode OLED|Flat Design,Dark productive,"Inter, IBM Plex Sans","Canvas UI, tool sidebar, color pickers","Decorative animations" |
| 43,Photography App Mobile,"photo-app,camera,filter,instagram-like",Dark Mode OLED,Dark Mode OLED|Flat Design,Near-black + accent,"SF Pro, Inter","Filter previews, camera UI","Heavy gradients" |
| 44,Music Streaming,"music,streaming,spotify-like,playlist,artist",Dark Mode OLED|Vaporwave,Dark Mode OLED|Vaporwave,Dark + vibrant accent,"Circular, Nunito","Album art dominant, waveform viz","Light themes" |
| 45,Podcast Platform,"podcast,audio,episode,rss,listener",Minimal & Direct|Dark Mode,Minimalism|Dark Mode OLED,Dark or clean white,"Libre Franklin, DM Sans","Episode list, waveform, subscribe","Visual clutter" |
| 46,Video Streaming,"video,streaming,netflix-like,watch,vod",Dark Mode OLED,Dark Mode OLED,Pure black #000,"Helvetica Neue, DM Sans","Thumbnail hover play, progress bar","Light or colorful bg" |
| 47,Gaming Platform,"gaming,game,esports,steam-like,multiplayer",Cyberpunk UI|Dark Mode,Cyberpunk UI|3D & Hyperrealism,Dark + neon green/orange,"Exo 2, Rajdhani","Game card hovers, neon glows, particle FX","Soft pastels" |
| 48,Indie Game Landing,"indie-game,game-landing,itch-io",Retro-Futurism|Brutalism,Retro-Futurism|Pixel Art,Game-specific thematic,"Pixel fonts or Clash Display","Game screenshots, trailer embed","Corporate clean" |
| 49,Web3 NFT,"web3,nft,blockchain,crypto-art,dao,ethereum",Glassmorphism|Cyberpunk,Glassmorphism|Cyberpunk UI,Dark + gradient accent,"Space Grotesk, Monument Extended","Wallet connect, grid galleries","Traditional trust signals" |
| 50,Social Network,"social,network,feed,post,community,twitter-like",Flat Design|Minimal,Flat Design|Soft UI Evolution,Clean white/dark,"Inter, SF Pro","Feed cards, notifications, follow","Heavy gradients" |
| 51,Community Forum,"forum,community,discussion,reddit-like,discord-like",Flat Design|Dark Mode,Flat Design|Dark Mode OLED,Clean dark or white,"Inter, Source Sans","Thread nesting, upvotes, user cards","Complex decoration" |
| 52,Dating App,"dating,match,swipe,tinder-like,romance",Hero-Centric|Vibrant,Vibrant & Block-based|Glassmorphism,Warm red-pink or sunset,"Nunito, Poppins","Swipe cards, match animation, chat","Clinical or corporate" |
| 53,Job Board,"job,hiring,recruitment,career,linkedin-like",Minimalism|Flat Design,Minimalism|Flat Design,Professional neutral,"Source Sans, DM Sans","Filter sidebar, job cards, apply flow","Heavy animation" |
| 54,Freelance Marketplace,"freelance,gig,fiverr-like,upwork-like,contractor",Social Proof-Focused,Social Proof-Focused|Flat Design,Trust warm neutral,"Inter, Open Sans","Skill tags, rating stars, portfolio grid","Overwhelming categories" |
| 55,Newsletter Tool,"newsletter,email,subscriber,substack-like,mailchimp",Minimal & Direct|Editorial,Minimalism|Editorial Grid,Warm editorial,"Lora, DM Sans","Subscribe hero, preview card, stats","Heavy interactive UI" |
| 56,Personal Blog,"blog,writing,article,personal-site,medium-like",Editorial Grid Magazine,Editorial Grid Magazine,Paper white or dark editorial,"Lora, Merriweather","Drop caps, reading time, related posts","Dashboard-style chrome" |
| 57,News Media,"news,journalism,newspaper,media,breaking",Editorial Grid Magazine,Editorial Grid Magazine,Newspaper B&W + 1 accent,"Libre Baskerville, Source Serif","Breaking ticker, section nav, photo essays","Flashy animations" |
| 58,Recipe Cooking,"recipe,cooking,food-blog,chef,ingredients",Hero-Centric Design|Editorial,Hero-Centric Design,Warm appetite,"Playfair Display, Lato","Food photography hero, step cards, print view","Dark or techy" |
| 59,Weather App,"weather,forecast,temperature,climate,meteorology",Soft UI Evolution|Glassmorphism,Glassmorphism|Soft UI Evolution,Sky blues and warm amber,"Nunito, DM Mono for temps","Animated weather icons, hourly scroll","Text-heavy data" |
| 60,Habit Tracker,"habit,tracker,streak,productivity,routine",Claymorphism|Gamification,Claymorphism|Vibrant,Bright motivating,"Nunito, Quicksand","Streak flame, calendar dots, celebration","Dark depressing themes" |
| 61,Meditation App,"meditation,mindfulness,breathing,calm-app,sleep",Ambient Calm UI,Ambient Calm UI|Organic Biophilic,Soft lavender-navy-warm white,"Nunito, Lato","Breathing circle, ambient sound viz","Harsh colors, fast animations" |
| 62,Diary Journal App,"diary,journal,writing,reflection,private",E-Ink Paper|Minimal,E-Ink Paper|Exaggerated Minimalism,Paper warm white or dark,"Lora, Merriweather","Lined paper feel, date nav","Social/sharing elements" |
| 63,Mood Tracker,"mood,emotion,mental-log,feeling",Claymorphism|Soft,Claymorphism|Soft UI Evolution,Warm pastel rainbow,"Nunito, Quicksand","Emoji mood scale, color-coded calendar","Clinical or dark" |
| 64,Home Automation,"smart-home,iot,automation,dashboard-home,alexa-like",HUD Sci-Fi FUI|Bento,Bento Box Grid|Dark Mode OLED,Dark slate + teal accent,"Inter, DM Mono","Device tiles, toggle animations, status rings","Colorful consumer" |
| 65,Startup Landing,"startup,mvp,launch,product-launch,early-stage",Conversion-Optimized|Hero-Centric,Hero-Centric Design|Minimal & Direct,Brand-specific focused,"Sora, DM Sans","Bold headline, waitlist form, social proof","Feature grid overload" |
| 66,Portfolio Personal,"personal,about-me,cv-online,personal-brand",Brutalism|Exaggerated Minimalism,Brutalism|Motion-Driven,Personal unique,"Variable/Clash Display","Strong type, project reveals, hover effects","Generic Bootstrap portfolio" |
| 67,Documentation Site,"docs,documentation,api-docs,developer-docs,readme",Minimalism|Dark Mode,Minimalism|Dark Mode OLED,Neutral with code accent,"Inter, JetBrains Mono","Sidebar nav, code blocks, search","Heavy visual decoration" |
| 68,Changelog Updates,"changelog,updates,release-notes,what-new",Minimalism|Flat,Minimalism|Flat Design,Clean white,"Inter, DM Sans","Timeline list, version badges","Complex interactive" |
| 69,Status Page,"status,uptime,incident,operational,downtime",Minimalism|Accessible,Minimalism|Accessible & Ethical,Neutral green/red semantic,"Inter, Source Sans","Status dots, incident timeline","Colorful branding" |
| 70,Admin Panel,"admin,backoffice,cms,management,crud",Data-Dense Dashboard,Bento Box Grid|Data-Dense,Neutral gray productive,"Inter, DM Sans","Sidebar + content, data tables, modals","Consumer-facing aesthetics" |
| 71,CRM Platform,"crm,customer,sales-crm,contacts,pipeline",Data-Dense Dashboard,Bento Box Grid|Data-Dense,Professional neutral,"Inter, DM Sans","Pipeline kanban, contact cards, activity feed","Heavy animation" |
| 72,HR Platform,"hr,human-resources,employees,onboarding,payroll",Soft UI Evolution,Soft UI Evolution|Bento Box Grid,Professional friendly,"Source Sans, Nunito","Org chart, vacation calendar, payroll table","Harsh corporate" |
| 73,Event Platform,"event,conference,ticket,registration,venue",Hero-Centric Design|Social Proof,Hero-Centric Design,Event-specific thematic,"Raleway, Open Sans","Countdown timer, speaker grid, venue map","Text-heavy listings" |
| 74,Auction Platform,"auction,bid,lot,sothebys-like,marketplace-auction",Trust & Authority|Editorial,Trust & Authority,Premium neutral-gold,"Libre Baskerville, DM Sans","Countdown bid timer, lot gallery","Playful or casual" |
| 75,Charity Nonprofit,"charity,nonprofit,donation,cause,ngo",Storytelling-Driven|Social Proof,Storytelling-Driven Landing,Warm emotional palette,"Merriweather, Open Sans","Impact numbers, photo stories, donate CTA","Cold corporate" |
| 76,Crowdfunding,"crowdfunding,kickstarter-like,fund,campaign,backer",Social Proof-Focused|Hero-Centric,Social Proof-Focused,Campaign-specific,"Nunito, Poppins","Progress bar, backer count, reward tiers","Generic marketplace" |
| 77,Real Estate Investment,"realestate-invest,reit,property-invest,crowdfund-property",Trust & Authority|Financial,Trust & Authority,Premium navy-gold,"Source Serif, DM Sans","ROI charts, property cards, investor CTA","Casual design" |
| 78,Pet Care,"pet,veterinary,dog,cat,pet-shop,grooming",Claymorphism|Vibrant,Claymorphism|Flat Design,Warm friendly animal palette,"Nunito, Quicksand","Pet profile cards, booking, breed search","Dark or clinical" |
| 79,Children App,"kids,children,toddler,educational-game,abc",Claymorphism,Claymorphism,Bright primary colors,"Fredoka One, Nunito","Large tap targets 56px+, sound FX, animations","Small targets, small text" |
| 80,Senior App,"senior,elderly,accessible,large-text,age60plus",Inclusive Design,Inclusive Design|Accessible & Ethical,High contrast clean,"Source Sans 18px+, Open Sans","48px+ buttons, simple nav, high contrast","Complex interactions" |
| 81,Telematics Fleet,"fleet,telematics,gps,vehicle,logistics",HUD Sci-Fi FUI|Data-Dense,Data-Dense Dashboard|HUD,Dark map with status overlays,"Roboto Mono, IBM Plex","Live map, vehicle status badges","Consumer aesthetics" |
| 82,Supply Chain,"supply-chain,logistics,warehouse,inventory,erp",Data-Dense Dashboard,Data-Dense Dashboard,Operational gray-blue,"IBM Plex, Source Sans","Gantt charts, inventory tables, alerts","Heavy decoration" |
| 83,Airport Travel Tech,"airport,flight,boarding,travel-tech,aviation",Minimalism|HUD,Minimalism|HUD Sci-Fi FUI,Clean blue-white high contrast,"DM Mono, Source Sans","Flight boards, departure grids","Low contrast" |
| 84,Climate Environment,"climate,environment,carbon,sustainability,eco",Nature Distilled|Organic,Nature Distilled|Organic Biophilic,Earth green-brown-sky,"Lora, Source Serif","Data viz for emissions, earth map","Neon or synthetic" |
| 85,Fitness Wearable App,"wearable,fitbit-like,healthdata,heart-rate,steps",Soft UI Evolution|Dark,Dark Mode OLED|Soft UI Evolution,Black + health green/orange,"DM Mono, Nunito","Ring stats, heart rate viz, daily summary","Text-heavy tables" |
| 86,Augmented Reality App,"ar,augmented-reality,spatial,lens,overlay",Spatial UI VisionOS,Spatial UI VisionOS|3D,Transparent overlays,"SF Pro, Inter","AR anchors, spatial panels, gesture hints","2D-only flat design" |
| 87,Voice Assistant App,"voice,siri-like,alexa-app,speech-ui",Voice-First Multimodal,Voice-First Multimodal|Zero Interface,Dark minimal,"SF Pro, Nunito","Waveform orb, listening animation","Text-heavy dense UI" |
| 88,Recruitment ATS,"ats,recruitment,hiring-tool,applicant-tracking",Data-Dense Dashboard,Data-Dense|Bento Box,Professional neutral,"Inter, Source Sans","Kanban pipeline, candidate cards, email threads","Consumer design" |
| 89,Logistics Delivery,"logistics,delivery,tracking,parcel,shipment",Flat Design|Real-Time Monitoring,Flat Design|Real-Time,Map-forward neutral,"DM Sans, Roboto Mono for tracking codes","Live map, status timeline, ETA","Heavy branding" |
| 90,Construction PM,"construction,building,project-pm,contractor",Flat Design|Data-Dense,Flat Design|Data-Dense,Industrial neutral,"Source Sans, IBM Plex","Gantt, site photos, punch list","Consumer-facing style" |
| 91,Legal Tech,"legaltech,contract,e-sign,compliance-tech,docusign-like",Minimalism|Trust,Trust & Authority|Minimalism,Professional navy-white,"Neue Haas, Source Serif","Document viewer, signature flow, redline","Playful colors" |
| 92,Prop Tech,"proptech,property-mgmt,landlord,tenant,rent",Flat Design|Social Proof,Social Proof|Flat Design,Clean blue-white,"DM Sans, Inter","Property cards, maintenance tickets","Overly luxurious" |
| 93,EdTech Assessment,"edtech,quiz,assessment,test,exam-platform",Claymorphism|Flat,Claymorphism|Flat Design,Encouraging bright,"Nunito, Open Sans","Progress indicator, question cards, score reveal","Test-anxiety inducing dark" |
| 94,Sports Analytics,"sports,stats,athlete,performance-data,scouting",Data-Dense|Vibrant,Data-Dense Dashboard|Vibrant,Dark + team accent,"DM Mono, Rajdhani","Radar charts, heatmaps, player cards","Generic BI" |
| 95,Esports Tournament,"esports,tournament,bracket,team,competitive",Cyberpunk UI|Dark,Cyberpunk UI|Vibrant,Dark neon brand accent,"Exo 2, Orbitron","Bracket tree, live score, team logos","Corporate clean" |
| 96,Agricultural Tech,"agtech,farming,crop,precision-agriculture,soil",Nature Distilled|Data-Dense,Nature Distilled|Data-Dense,Earth green-amber,"Source Serif, DM Sans","Field maps, sensor data, weather overlay","Urban tech dark themes" |
| 97,Mining Resources,"mining,resources,extraction,geology",HUD|Data-Dense,HUD Sci-Fi FUI|Data-Dense,Industrial dark-amber,"IBM Plex Mono, Source Sans","Equipment status, sensor readings","Consumer-facing design" |
| 98,Energy Utility,"energy,utility,grid,power,solar,wind",Data-Dense|Environmental,Data-Dense|Nature Distilled,Clean functional blue-green,"Source Sans, DM Mono","Energy flow diagrams, meter readings","Heavy branding" |
| 99,Telecom Carrier,"telecom,carrier,network,5g,mobile-operator",Data-Dense|Trust,Trust & Authority|Data-Dense,Corporate blue-white,"Source Sans, Helvetica","Network map, coverage checker, plan tables","Dark/gaming aesthetics" |
| 100,Pharmaceutical,"pharma,drug-discovery,clinical-trial,biotech",Accessible & Ethical|Minimalism,Accessible & Ethical,Clinical clean white-blue,"Source Sans, IBM Plex","Trial data tables, molecular viz","Playful or colorful" |
| 101,Space Tech,"space,aerospace,satellite,rocket,nasa-like",HUD Sci-Fi FUI|Dark,HUD Sci-Fi FUI|3D,Dark navy-black + cyan,"Orbitron, Space Grotesk","Orbit viz, telemetry, star map bg","Consumer light themes" |
| 102,Quantum Computing,"quantum,qubit,computing-research",HUD Sci-Fi FUI,HUD Sci-Fi FUI|Dark Mode,Dark + quantum blue-purple,"IBM Plex Mono, Space Grotesk","Wave function viz, qubit state","Plain corporate" |
| 103,Autonomous Vehicle,"autonomous,self-driving,av,robotaxi,lidar",HUD Sci-Fi FUI|Real-Time,HUD Sci-Fi FUI|Real-Time Monitoring,Dark HUD blue,"DM Mono, IBM Plex","Bird's-eye map, sensor overlay, status","Consumer friendly colors" |
| 104,Drone Fleet,"drone,uav,fleet-mgmt,aerial,surveillance",HUD Sci-Fi FUI|Data-Dense,HUD Sci-Fi FUI|Real-Time,Dark operational,"Roboto Mono, DM Mono","Live map, battery status, flight path","Consumer aesthetics" |
| 105,Crypto Exchange,"exchange,dex,trading,order-book,swap",Dark Mode|Financial,Dark Mode OLED|Financial Dashboard,Dark + green-red trading,"Roboto Mono, Space Grotesk","Order book, candlestick, depth chart","Friendly consumer colors" |
| 106,NFT Marketplace,"nft,marketplace,opensea-like,mint,collectible",Glassmorphism|Cyberpunk,Glassmorphism|Dark Mode,Dark + vibrant,"Space Grotesk, Monument Extended","Grid gallery, rarity badge, wallet connect","Traditional e-commerce" |
| 107,DAO Governance,"dao,governance,voting,proposal,decentralized",Minimalism|Dark,Minimalism|Dark Mode,Dark neutral-green,"DM Mono, IBM Plex","Proposal cards, vote bars, treasury stats","Corporate branding" |
| 108,Metaverse Platform,"metaverse,virtual-world,avatar,vr-social",3D & Hyperrealism|Spatial,3D & Hyperrealism|Spatial UI,Dark immersive + avatar accent,"Space Grotesk, Orbitron","3D world preview, avatar builder","Flat 2D only" |
| 109,Productivity Suite,"productivity,office,docs,spreadsheet,workspace",Soft UI Evolution|Minimalism,Soft UI Evolution|Minimalism,Clean neutral gray-white,"Inter, DM Sans","Toolbar, document canvas, collaboration cursors","Heavy branding" |
| 110,Password Manager,"password,security,vault,credentials,1password-like",Dark Mode|Trust,Dark Mode OLED|Trust & Authority,Dark secure + green accent,"JetBrains Mono, DM Sans","Strength meter, vault list, copy-to-clipboard","Colorful playful" |
| 111,VPN Privacy App,"vpn,privacy,security-app,proxy,anonymous",Dark Mode|HUD,Dark Mode OLED|HUD Sci-Fi,Dark + secure green/cyan,"DM Mono, Space Grotesk","Globe connection viz, server list","Bright consumer colors" |
| 112,File Storage Cloud,"cloud-storage,drive,files,s3-like,dropbox-like",Flat Design|Minimalism,Flat Design|Minimalism,Clean white-blue,"Inter, Source Sans","File browser, upload progress, shared links","Heavy gradients" |
| 113,Email Client,"email,inbox,mail,client,gmail-like",Minimalism|Flat Design,Minimalism|Flat Design,Clean productive white,"Inter, DM Sans","Thread list, compose, label sidebar","Colorful distracting" |
| 114,Calendar Scheduling,"calendar,scheduling,booking,time,appointment-mgmt",Flat Design|Soft UI,Flat Design|Soft UI Evolution,Clean time-based neutral,"Inter, DM Sans","Month/week grid, drag events, timezone","Heavy decoration" |
| 115,Map Navigation,"map,navigation,gps,geo,mapbox-like",Dark Mode|Real-Time,Dark Mode OLED|Real-Time Monitoring,Dark map + action color,"DM Mono, Roboto","Map tiles, route overlay, POI pins","Light map on dark chrome" |
| 116,Chat Messaging,"chat,messaging,dm,team-chat,slack-like",Flat Design|Dark Mode,Flat Design|Dark Mode OLED,Dark or white clean,"Inter, Nunito","Message bubbles, reactions, thread","Heavy branding decoration" |
| 117,Video Conferencing,"video-call,conference,zoom-like,meeting,webrtc",Minimal & Direct|Dark,Minimalism|Dark Mode,Dark functional,"Inter, DM Sans","Video grid, mute/cam controls, screen share","Complex UI" |
| 118,E-signature,"e-sign,signature,document-sign,docusign-like,contract-sign",Trust & Authority|Minimalism,Trust & Authority,Clean white professional,"Source Serif, Source Sans","Document viewer, signature field, audit trail","Playful or colorful" |
| 119,Survey Form Tool,"survey,form,questionnaire,typeform-like,response",Minimalism|Claymorphism,Minimalism|Claymorphism,Clean or warm friendly,"Nunito, DM Sans","One-question-at-a-time, progress, animation","Overwhelming form dump" |
| 120,A/B Testing Tool,"abtesting,experiment,optimization,cro-tool,split-test",Data-Dense|Minimalism,Data-Dense|Minimalism,Clean data-forward,"Inter, DM Mono","Variant comparison, significance meter","Heavy decoration" |
| 121,Customer Support,"support,helpdesk,tickets,zendesk-like,customer-service",Flat Design|Soft UI,Flat Design|Soft UI Evolution,Friendly professional,"Source Sans, Open Sans","Ticket queue, status badge, CSAT stars","Intimidating corporate" |
| 122,Live Chat Widget,"live-chat,chat-widget,intercom-like,support-chat",Soft UI Evolution,Soft UI Evolution|Glassmorphism,White + brand accent,"Inter, Nunito","Chat bubble, typing indicator, emoji","Dark or complex" |
| 123,Push Notification Tool,"push,notification,engagement,onesignal-like",Flat Design|Minimal,Flat Design|Minimalism,Clean brand-neutral,"Inter, DM Sans","Preview cards, audience targeting UI","Heavy visualization" |
| 124,Subscription Management,"subscription,manage,billing-portal,stripe-like",Minimalism|Trust,Minimalism|Trust & Authority,Clean professional,"Inter, Source Sans","Plan cards, usage meter, invoice list","Colorful decorative" |
| 125,Affiliate Marketing,"affiliate,referral,commission,program,partner",Social Proof|Conversion,Social Proof-Focused|Conversion,Trust warm,"Nunito, Open Sans","Commission table, referral link, leaderboard","Complex dashboards" |
| 126,Influencer Platform,"influencer,creator,ugc,brand-deal,sponsorship",Social Proof|Vibrant,Social Proof|Vibrant & Block,Creator energy palette,"Poppins, Nunito","Profile cards, reach stats, deal board","Corporate cold" |
| 127,Learning Management,"lms,learning,course-platform,moodle-like,canvas-lms",Claymorphism|Flat Design,Claymorphism|Flat Design,Friendly educational,"Nunito, Open Sans","Course cards, progress bars, quiz","Dark intimidating" |
| 128,Certification Platform,"certification,badge,credential,certificate,accreditation",Trust & Authority|Flat,Trust & Authority|Flat Design,Professional credible,"Source Serif, DM Sans","Badge showcase, exam flow, verify link","Overly playful" |
| 129,Talent Assessment,"talent,assessment,psychometric,hiring-test,skills-test",Accessible & Ethical|Minimal,Accessible & Ethical|Minimalism,Clean neutral,"Source Sans, IBM Plex","Timed test UI, question types, results","Stressful dark themes" |
| 130,Knowledge Base,"knowledge-base,wiki,help-center,docs-kb,faq",Minimalism|Flat Design,Minimalism|Flat Design,Clean white-gray,"Inter, Source Sans","Search-first, category cards, breadcrumb","Cluttered decoration" |
| 131,Internal Tools,"internal,admin-tool,back-office,ops-tool",Data-Dense Dashboard,Bento Box Grid|Data-Dense,Neutral operational gray,"Inter, IBM Plex","CRUD tables, bulk actions, filter sidebar","Consumer branding" |
| 132,Monitoring Observability,"monitoring,observability,logs,metrics,grafana-like",Real-Time Monitoring|Dark,Real-Time Monitoring|Dark Mode,Dark + signal green/orange,"DM Mono, IBM Plex Mono","Live metric charts, alert panels, log streams","Consumer aesthetics" |
| 133,Data Pipeline ETL,"etl,pipeline,data-engineering,airflow-like,workflow",Data-Dense|HUD,Data-Dense Dashboard|HUD,Dark or neutral technical,"IBM Plex Mono, DM Mono","DAG visualization, run status, logs","Heavy branding" |
| 134,Business Intelligence,"bi,business-intelligence,power-bi-like,tableau-like",Data-Dense Dashboard,Data-Dense Dashboard|Financial,Professional data-forward,"Inter, DM Mono","Drill-down charts, filter sliders, export","Consumer fun colors" |
| 135,IoT Platform,"iot,sensors,devices,mqtt,telemetry",Real-Time Monitoring|HUD,Real-Time Monitoring|HUD Sci-Fi,Dark dashboard + status colors,"DM Mono, IBM Plex","Device grid, sensor sparklines, alert feed","Light consumer" |
| 136,Robotics Platform,"robotics,robot,automation,ros,manufacturing-robot",HUD Sci-Fi FUI|Data-Dense,HUD Sci-Fi FUI|Data-Dense,Dark operational slate,"Roboto Mono, DM Mono","Joint visualization, status panel, telemetry","Consumer friendly" |
| 137,Digital Twin,"digital-twin,simulation,3d-model,virtual-replica",HUD Sci-Fi FUI|3D,HUD Sci-Fi FUI|3D & Hyperrealism,Dark with 3D accent,"Space Grotesk, DM Mono","3D model viewer, sensor overlay","Flat 2D" |
| 138,Blockchain Explorer,"blockchain,explorer,transactions,on-chain,etherscan-like",Dark Mode|Data-Dense,Dark Mode OLED|Data-Dense,Dark + blue-green technical,"JetBrains Mono, IBM Plex Mono","Transaction table, block list, hash display","Consumer design" |
| 139,Social Commerce,"social-commerce,shoppable,instagram-shop,tiktok-shop",Social Proof|Vibrant,Social Proof|Vibrant,Warm social-native,"Nunito, Poppins","UGC grid, shoppable tags, quick checkout","Traditional e-commerce" |
| 140,Reseller Platform,"reseller,white-label,partner-portal,distributor",Trust & Authority|Flat,Trust & Authority|Flat Design,Professional neutral,"Source Sans, DM Sans","Catalog, pricing tiers, order management","Consumer-facing design" |
| 141,Auction House,"auction-house,fine-art,bidding,lot,christie-like",Editorial Grid|Trust,Editorial Grid Magazine|Trust & Authority,Monochrome + gold,"Cormorant Garamond, Libre Baskerville","Lot viewer, countdown, bid history","Colorful or playful" |
| 142,Tax Software,"tax,irs,return,tax-prep,turbotax-like",Trust & Authority|Accessible,Trust & Authority|Accessible & Ethical,Conservative blue-white,"Source Sans, IBM Plex","Step-by-step wizard, form fields, refund estimate","Casual colors" |
| 143,Insurance Comparison,"insurance-compare,quotes,compare,aggregator",Social Proof|Trust,Social Proof|Trust & Authority,Clean trust-forward,"Source Sans, DM Sans","Quote cards, comparison table, trust badges","Complex dark" |
| 144,Mortgage Calculator,"mortgage,loan,calculator,home-loan,refinance",Trust & Authority|Minimalism,Trust & Authority|Minimalism,Professional clean,"Source Serif, Source Sans","Input sliders, amortization chart, CTA","Playful" |
| 145,Subscription Billing,"billing,subscriptions,invoicing,recurly-like,chargebee",Minimalism|Data-Dense,Minimalism|Data-Dense,Clean professional,"Inter, DM Sans","MRR metrics, invoice list, plan management","Heavy decoration" |
| 146,Customer Data Platform,"cdp,customer-data,profiles,segmentation,mparticle-like",Data-Dense|Bento,Data-Dense Dashboard|Bento Box,Data professional neutral,"Inter, DM Mono","Segment builder, profile cards, data flow viz","Consumer-friendly" |
| 147,Content Management,"cms,content,wordpress-like,headless,website-editor",Flat Design|Minimalism,Flat Design|Minimalism,Clean editorial neutral,"Inter, Source Serif","Content editor, media library, publish flow","Heavy animation" |
| 148,Product Roadmap,"roadmap,product-planning,feature-request,aha-like",Bento Box Grid|Flat,Bento Box Grid|Flat Design,Clean product-forward,"Inter, DM Sans","Timeline roadmap, vote widget, now/next/later","Overly complex" |
| 149,Customer Success,"cs,customer-success,onboarding,churn,health-score",Soft UI Evolution|Bento,Soft UI Evolution|Bento Box,Friendly professional,"Nunito, Source Sans","Health score gauge, playbook cards, alert feed","Cold corporate" |
| 150,Partner Ecosystem,"partner,ecosystem,marketplace,integration,app-store",Flat Design|Social Proof,Flat Design|Social Proof,Clean trust-forward,"Inter, DM Sans","Integration cards, partner levels, API status","Heavy branding" |
| 151,Loyalty Rewards,"loyalty,rewards,points,gamification,program",Claymorphism|Vibrant,Claymorphism|Vibrant,Warm rewarding palette,"Nunito, Poppins","Point balance, reward cards, progress to tier","Cold corporate" |
| 152,Event Ticketing,"ticketing,events-tickets,concert,venue,eventbrite-like",Hero-Centric|Social Proof,Hero-Centric Design|Social Proof,Event-thematic vibrant,"Raleway, Open Sans","Event hero, seat map, countdown","Plain listing" |
| 153,Gig Economy,"gig,rideshare,uber-like,task,on-demand",Flat Design|Real-Time,Flat Design|Real-Time Monitoring,Clean map-forward,"DM Sans, Inter","Live map, request flow, rating","Heavy decoration" |
| 154,Coworking Space,"coworking,office,desk-booking,hot-desk,workspace",Soft UI Evolution|Bento,Soft UI Evolution|Bento Box,Modern warm neutral,"Inter, Nunito","Floor map, desk booking calendar, amenity cards","Cold corporate" |
| 155,Mental Wellness B2B,"eap,employee-wellness,mental-health-b2b,wellbeing",Organic Biophilic|Accessible,Organic Biophilic|Accessible & Ethical,Calming earth tones,"Nunito, Source Sans","Mood check-in, resource cards, usage stats","Clinical or harsh" |
| 156,Compliance Regulatory,"compliance,regulatory,risk,audit,grc",Trust & Authority|Data-Dense,Trust & Authority|Data-Dense,Corporate professional navy,"Source Serif, IBM Plex","Compliance checklist, risk heat map, audit log","Consumer aesthetics" |
| 157,Learning Experience,"lxp,personalized-learning,skill-path,growth",Claymorphism|Bento,Claymorphism|Bento Box Grid,Encouraging warm-bright,"Nunito, Poppins","Skill graph, learning path, achievement badges","Dark or intimidating" |
| 158,Digital Signage,"signage,display,kiosk,digital-screen,ooh",Dark Mode OLED|Vibrant,Dark Mode OLED|Vibrant,High contrast vivid,"Exo 2, Source Sans (large)","Large text, bold graphics, looping animations","Small text, complex UI" |
| 159,Smart City Platform,"smart-city,urban,city-data,gov-tech,municipality",Data-Dense|HUD,Data-Dense Dashboard|HUD Sci-Fi,Dark gov-tech blue-gray,"IBM Plex, DM Mono","City map, KPI tiles, incident feed","Consumer-friendly" |
| 160,Research Platform,"research,academic,data-collection,survey-research",Minimalism|Accessible,Minimalism|Accessible & Ethical,Clean academic white,"Merriweather, Source Sans","Citation tools, data viz, export","Heavy branding" |
| 161,Quantum Computing Interface,"quantum-ui,qubit-viz,quantum-dashboard",HUD Sci-Fi FUI|Dark,HUD Sci-Fi FUI|Dark Mode,Dark quantum blue-purple,"IBM Plex Mono, Space Grotesk","Bloch sphere viz, circuit diagram, probability wave","Plain tables" |
| """ |
|
|
| |
| _TYPOGRAPHY_CSV = """id,pairing,display_font,body_font,mood,best_for,google_fonts_url |
| 1,Sora + Inter,Sora,Inter,Modern clean tech,SaaS / AI / Developer tools,https://fonts.google.com/share?selection.family=Sora:wght@400;600;700|Inter:wght@400;500;600 |
| 2,Playfair Display + Lato,Playfair Display,Lato,Elegant editorial,Luxury / Restaurant / Beauty spa,https://fonts.google.com/share?selection.family=Playfair+Display:wght@400;600;700|Lato:wght@300;400;700 |
| 3,Space Grotesk + JetBrains Mono,Space Grotesk,JetBrains Mono,Technical premium,AI / Crypto / Dev tools,https://fonts.google.com/share?selection.family=Space+Grotesk:wght@400;500;700|JetBrains+Mono:wght@400;500 |
| 4,Cormorant Garamond + Montserrat,Cormorant Garamond,Montserrat,Refined luxurious,Luxury / Legal / Architecture,https://fonts.google.com/share?selection.family=Cormorant+Garamond:wght@400;600|Montserrat:wght@400;500;600 |
| 5,Geist + Geist Mono,Geist,Geist Mono,Dev-first clean,Developer tools / Documentation,https://fonts.google.com/share?selection.family=Geist:wght@400;500;600|Geist+Mono:wght@400;500 |
| 6,Nunito + Open Sans,Nunito,Open Sans,Friendly approachable,Educational / Children / Wellness,https://fonts.google.com/share?selection.family=Nunito:wght@400;600;700|Open+Sans:wght@400;500;600 |
| 7,Clash Display + Satoshi,Clash Display,Satoshi,Gen Z creative,Creative agency / Brand / Gen Z,https://fonts.google.com/share?selection.family=Clash+Display:wght@400;600;700 |
| 8,DM Serif Display + DM Sans,DM Serif Display,DM Sans,Editorial modern,Blog / News / Editorial,https://fonts.google.com/share?selection.family=DM+Serif+Display|DM+Sans:wght@400;500;600 |
| 9,Libre Baskerville + Source Sans 3,Libre Baskerville,Source Sans 3,Traditional trustworthy,Legal / Finance / Government,https://fonts.google.com/share?selection.family=Libre+Baskerville:wght@400;700|Source+Sans+3:wght@400;500;600 |
| 10,Exo 2 + Rajdhani,Exo 2,Rajdhani,Energetic sporty,Gaming / Fitness / Esports,https://fonts.google.com/share?selection.family=Exo+2:wght@400;600;700|Rajdhani:wght@400;500;600 |
| 11,Merriweather + Source Sans 3,Merriweather,Source Sans 3,Scholarly readable,Academic / Research / Journalism,https://fonts.google.com/share?selection.family=Merriweather:wght@400;700|Source+Sans+3:wght@400;500 |
| 12,Poppins + Lato,Poppins,Lato,Versatile modern,E-commerce / Startup / Lifestyle,https://fonts.google.com/share?selection.family=Poppins:wght@400;500;600;700|Lato:wght@300;400;700 |
| 13,IBM Plex Serif + IBM Plex Sans,IBM Plex Serif,IBM Plex Sans,Technical editorial,B2B / Enterprise / Pharma,https://fonts.google.com/share?selection.family=IBM+Plex+Serif:wght@400;600|IBM+Plex+Sans:wght@400;500;600 |
| 14,Raleway + Open Sans,Raleway,Open Sans,Elegant versatile,Hotel / Travel / Events,https://fonts.google.com/share?selection.family=Raleway:wght@400;500;700|Open+Sans:wght@400;500 |
| 15,Fredoka One + Quicksand,Fredoka One,Quicksand,Playful fun,Children / Pet care / Games,https://fonts.google.com/share?selection.family=Fredoka+One|Quicksand:wght@400;500;600 |
| 16,Neue Haas Grotesk + Helvetica Neue,Neue Haas Grotesk,Helvetica Neue,Swiss professional,Corporate / Banking / Insurance,https://fonts.google.com/ |
| 17,Monument Extended + Space Grotesk,Monument Extended,Space Grotesk,Bold crypto,Web3 / NFT / Crypto,https://fonts.google.com/share?selection.family=Space+Grotesk:wght@400;500;700 |
| 18,Orbitron + Space Grotesk,Orbitron,Space Grotesk,Sci-fi futuristic,Space tech / Gaming / HUD,https://fonts.google.com/share?selection.family=Orbitron:wght@400;600;700|Space+Grotesk:wght@400;500 |
| 19,Circular + Nunito,Circular,Nunito,Music streaming,Music / Podcast / Media,https://fonts.google.com/share?selection.family=Nunito:wght@400;500;600 |
| 20,Lora + DM Sans,Lora,DM Sans,Editorial warm,Newsletter / Blog / Recipe,https://fonts.google.com/share?selection.family=Lora:wght@400;500;600|DM+Sans:wght@400;500 |
| 21,Syne + Inter,Syne,Inter,Modern bold,Creative portfolio / Agency,https://fonts.google.com/share?selection.family=Syne:wght@400;600;700|Inter:wght@400;500;600 |
| 22,Bricolage Grotesque + Inter,Bricolage Grotesque,Inter,Contemporary fresh,Startup / SaaS / Product,https://fonts.google.com/share?selection.family=Bricolage+Grotesque:wght@400;500;600;700|Inter:wght@400;500 |
| 23,Cabinet Grotesk + Satoshi,Cabinet Grotesk,Satoshi,Premium modern,Fintech / SaaS / Premium,https://fonts.google.com/share?selection.family=Cabinet+Grotesk:wght@400;500;700 |
| 24,Fraunces + Inter,Fraunces,Inter,Quirky editorial,Brand / Blog / Creative,https://fonts.google.com/share?selection.family=Fraunces:wght@400;600;700|Inter:wght@400;500 |
| 25,Plus Jakarta Sans + DM Sans,Plus Jakarta Sans,DM Sans,Clean productive,Productivity / SaaS / Clean,https://fonts.google.com/share?selection.family=Plus+Jakarta+Sans:wght@400;500;600;700|DM+Sans:wght@400;500 |
| 26,Anybody + Inter,Anybody,Inter,Variable expressive,Creative / Experimental,https://fonts.google.com/share?selection.family=Anybody:wght@400;500;700|Inter:wght@400;500 |
| 27,Bebas Neue + Open Sans,Bebas Neue,Open Sans,Bold impactful,Fitness / Sports / Marketing,https://fonts.google.com/share?selection.family=Bebas+Neue|Open+Sans:wght@400;500;600 |
| 28,Oswald + Source Sans 3,Oswald,Source Sans 3,Strong editorial,News / Sports / Event,https://fonts.google.com/share?selection.family=Oswald:wght@400;500;600|Source+Sans+3:wght@400;500 |
| 29,Italiana + Lato,Italiana,Lato,High fashion,Fashion / Luxury / Beauty,https://fonts.google.com/share?selection.family=Italiana|Lato:wght@300;400;700 |
| 30,Spectral + Source Sans 3,Spectral,Source Sans 3,Literary refined,Publishing / Legal / Academic,https://fonts.google.com/share?selection.family=Spectral:wght@400;500;600|Source+Sans+3:wght@400;500 |
| 31,Work Sans + Roboto,Work Sans,Roboto,Utilitarian clear,Internal tools / Admin,https://fonts.google.com/share?selection.family=Work+Sans:wght@400;500;600|Roboto:wght@400;500 |
| 32,Josefin Sans + Josefin Slab,Josefin Sans,Josefin Slab,Art deco vintage,Art / Design / Vintage brand,https://fonts.google.com/share?selection.family=Josefin+Sans:wght@400;600|Josefin+Slab:wght@400;600 |
| 33,Rubik + Rubik,Rubik,Rubik,Modern rounded,Mobile / App / SaaS,https://fonts.google.com/share?selection.family=Rubik:wght@400;500;600;700 |
| 34,Manrope + Manrope,Manrope,Manrope,Elegant uniform,Design tool / Portfolio / SaaS,https://fonts.google.com/share?selection.family=Manrope:wght@400;500;600;700 |
| 35,Fira Code + Fira Sans,Fira Code,Fira Sans,Developer focused,Code / Dev tool / Technical,https://fonts.google.com/share?selection.family=Fira+Code:wght@400;500|Fira+Sans:wght@400;500;600 |
| 36,Righteous + Roboto,Righteous,Roboto,Retro fun,Gaming / Entertainment / Youth,https://fonts.google.com/share?selection.family=Righteous|Roboto:wght@400;500 |
| 37,Unbounded + Space Grotesk,Unbounded,Space Grotesk,Web3 bold,Web3 / NFT / Crypto / Bold tech,https://fonts.google.com/share?selection.family=Unbounded:wght@400;600;700|Space+Grotesk:wght@400;500 |
| 38,Red Hat Display + Red Hat Text,Red Hat Display,Red Hat Text,Open source tech,Developer / Open source / Docs,https://fonts.google.com/share?selection.family=Red+Hat+Display:wght@400;500;700|Red+Hat+Text:wght@400;500 |
| 39,Barlow + Barlow Semi Condensed,Barlow,Barlow Semi Condensed,Industrial condensed,Construction / Logistics / Auto,https://fonts.google.com/share?selection.family=Barlow:wght@400;500;600|Barlow+Semi+Condensed:wght@400;500 |
| 40,Kanit + Sarabun,Kanit,Sarabun,Modern Thai-friendly,Southeast Asia / Global,https://fonts.google.com/share?selection.family=Kanit:wght@400;500;600|Sarabun:wght@400;500 |
| 41,Staatliches + Roboto Condensed,Staatliches,Roboto Condensed,Brutalist poster,Art / Music / Event poster,https://fonts.google.com/share?selection.family=Staatliches|Roboto+Condensed:wght@400;700 |
| 42,Cinzel + Raleway,Cinzel,Raleway,Classical roman,Luxury / Legal / Historical,https://fonts.google.com/share?selection.family=Cinzel:wght@400;600;700|Raleway:wght@400;500 |
| 43,Audiowide + Exo 2,Audiowide,Exo 2,Tech scifi,Gaming / Drone / Space,https://fonts.google.com/share?selection.family=Audiowide|Exo+2:wght@400;500;600 |
| 44,Cardo + Raleway,Cardo,Raleway,Classic editorial,Literature / Publishing / Art,https://fonts.google.com/share?selection.family=Cardo:wght@400;700|Raleway:wght@400;500 |
| 45,Lexend + Lexend,Lexend,Lexend,Reading optimized,Accessibility / Reading / Education,https://fonts.google.com/share?selection.family=Lexend:wght@400;500;600;700 |
| 46,Bai Jamjuree + Sarabun,Bai Jamjuree,Sarabun,Contemporary Thai,Thailand / SE Asia market,https://fonts.google.com/share?selection.family=Bai+Jamjuree:wght@400;500;600|Sarabun:wght@400;500 |
| 47,Comfortaa + Nunito,Comfortaa,Nunito,Soft rounded,Children / Wellness / Friendly,https://fonts.google.com/share?selection.family=Comfortaa:wght@400;500;700|Nunito:wght@400;500;600 |
| 48,Alfa Slab One + Raleway,Alfa Slab One,Raleway,Bold serif impact,Sports / Newspaper / Retail,https://fonts.google.com/share?selection.family=Alfa+Slab+One|Raleway:wght@400;500;600 |
| 49,Bitter + Hind,Bitter,Hind,South Asian editorial,India / Bangladesh market,https://fonts.google.com/share?selection.family=Bitter:wght@400;500;700|Hind:wght@400;500;600 |
| 50,Vollkorn + Source Sans 3,Vollkorn,Source Sans 3,Scholarly warm,Academic / Research / Nonprofit,https://fonts.google.com/share?selection.family=Vollkorn:wght@400;600|Source+Sans+3:wght@400;500 |
| 51,Bodoni Moda + DM Sans,Bodoni Moda,DM Sans,Luxury contrast,Fashion / High-end / Premium,https://fonts.google.com/share?selection.family=Bodoni+Moda:wght@400;600|DM+Sans:wght@400;500 |
| 52,Secular One + Rubik,Secular One,Rubik,Bold Hebrew-friendly,Middle East / International,https://fonts.google.com/share?selection.family=Secular+One|Rubik:wght@400;500 |
| 53,BioRhyme + Open Sans,BioRhyme,Open Sans,Organic natural,Biotech / Organic / Health,https://fonts.google.com/share?selection.family=BioRhyme:wght@400;700|Open+Sans:wght@400;500 |
| 54,Epilogue + Inter,Epilogue,Inter,Contemporary editorial,Newsletter / Modern media,https://fonts.google.com/share?selection.family=Epilogue:wght@400;500;600;700|Inter:wght@400;500 |
| 55,Figtree + Inter,Figtree,Inter,Friendly geometric,SaaS / Product / Clean modern,https://fonts.google.com/share?selection.family=Figtree:wght@400;500;600;700|Inter:wght@400;500 |
| 56,Outfit + DM Sans,Outfit,DM Sans,Rounded modern,App / Startup / Modern product,https://fonts.google.com/share?selection.family=Outfit:wght@400;500;600;700|DM+Sans:wght@400;500 |
| 57,Kumbh Sans + Noto Sans,Kumbh Sans,Noto Sans,Global multilingual,International / Multi-language,https://fonts.google.com/share?selection.family=Kumbh+Sans:wght@400;500;600|Noto+Sans:wght@400;500 |
| """ |
|
|
| |
| _CHARTS_CSV = """id,chart_type,use_case,library,keywords |
| 1,Line Chart,Time-series trends,Recharts / Chart.js,trend timeline progress |
| 2,Area Chart,Volume over time,Recharts / ApexCharts,area filled cumulative |
| 3,Bar Chart,Category comparison,Recharts / Chart.js,comparison ranking bars |
| 4,Stacked Bar,Part-to-whole over time,Recharts / ApexCharts,stacked segments breakdown |
| 5,Horizontal Bar,Long category labels,Chart.js / Recharts,horizontal ranking leaderboard |
| 6,Pie Chart,Simple proportion,Chart.js,proportion share percentage simple |
| 7,Donut Chart,Proportion + center metric,Recharts / ApexCharts,donut ring center kpi |
| 8,Scatter Plot,Correlation between variables,D3.js / Recharts,scatter correlation cluster |
| 9,Bubble Chart,3-variable comparison,D3.js / Recharts,bubble size dimension |
| 10,Heatmap,Matrix density,D3.js / Nivo,heatmap matrix density intensity |
| 11,Treemap,Hierarchical proportion,D3.js / Nivo,treemap hierarchy nested proportion |
| 12,Funnel Chart,Conversion stages,ApexCharts / Funnel.js,funnel conversion drop-off stages |
| 13,Gauge / Radial,Single KPI vs target,ApexCharts / D3,gauge radial meter speedometer kpi |
| 14,Candlestick,Financial OHLC data,ApexCharts / Lightweight Charts,candlestick ohlc stock trading financial |
| 15,Waterfall Chart,Sequential change,ApexCharts / D3,waterfall delta change flow profit |
| 16,Sankey Diagram,Flow between nodes,D3.js,sankey flow alluvial transition |
| 17,Radar / Spider,Multi-dimension comparison,Recharts / ApexCharts,radar spider polygon multi-dimension skill |
| 18,Gantt Chart,Project timeline,DHTMLX / Frappe Gantt,gantt project timeline schedule |
| 19,Network Graph,Relationship nodes,D3.js / Sigma.js,network graph nodes edges relationship |
| 20,Choropleth Map,Geographic data,Leaflet / Mapbox / D3,map geographic choropleth region country |
| 21,Histogram,Distribution,Chart.js / D3,histogram distribution frequency bucket |
| 22,Box Plot,Statistical distribution,D3.js / ApexCharts,box plot whisker outlier distribution quartile |
| 23,Chord Diagram,Bidirectional flow,D3.js,chord flow matrix bidirectional |
| 24,Timeline,Events over time,vis.js / D3,timeline events history chronological |
| 25,Word Cloud,Text frequency,D3-cloud,wordcloud text frequency tag |
| """ |
|
|
| |
| _UX_GUIDELINES = """ |
| ═══ 99 UX GUIDELINES (UI/UX PRO MAX v2.5) ═══ |
| |
| LAYOUT (Rules 1-20): |
| 1. Mobile-first breakpoints: 375px → 768px → 1024px → 1440px |
| 2. 8px grid system: spacing in 4/8/16/24/32/48/64px |
| 3. Max content width: 1280px centered with auto margins |
| 4. Sidebar: 240-280px desktop, drawer on mobile |
| 5. Header height: 52-64px desktop, 52px mobile |
| 6. Card padding: 16-24px consistent |
| 7. Section padding: 64-96px vertical on desktop, 40-48px mobile |
| 8. CTA button min-height: 44px (touch target) |
| 9. Form label above input (not placeholder-only) |
| 10. Sticky header max 64px to preserve viewport |
| 11. Footer: full-width, 3-4 columns desktop, stacked mobile |
| 12. Grid columns: 12-col desktop, 8-col tablet, 4-col mobile |
| 13. Hero section: 100vh or min 540px |
| 14. Modal max-width: 560px, centered with backdrop |
| 15. Tooltip max-width: 200px |
| 16. Dropdown max-height: 320px with scroll |
| 17. Z-index scale: 10 base, 100 dropdown, 200 modal, 300 toast |
| 18. Negative space ratio: 40%+ for premium feel |
| 19. Left-to-right reading flow: most important element top-left |
| 20. F-pattern scan: headline → subhead → body → CTA |
| |
| INTERACTIONS (Rules 21-40): |
| 21. cursor:pointer on ALL clickable elements — no exceptions |
| 22. Hover transition: 150-200ms ease for most elements |
| 23. Button press: scale(0.97) or scale(0.98) active state |
| 24. Link underline on hover at minimum |
| 25. Loading skeleton for all async data fetches |
| 26. Empty state: illustration + helpful message + CTA |
| 27. Error state: red border, error text below, no shake |
| 28. Success state: green check, auto-dismiss toast 3s |
| 29. Disabled: opacity 0.4-0.5, cursor:not-allowed |
| 30. Focus ring: 2px solid + 2px offset (visible keyboard nav) |
| 31. Scroll restoration on route change |
| 32. Infinite scroll: show spinner, load more button at 80% scroll |
| 33. Drag and drop: ghost opacity 0.5, drop zone highlight |
| 34. Form validation: on blur (not on every keystroke) |
| 35. Password field: show/hide toggle |
| 36. Search: debounce 300ms, clear button, loading indicator |
| 37. Select all: checkbox in table header |
| 38. Tooltip: delay 400ms show, 100ms hide |
| 39. Context menu: right-click or 3-dot kebab |
| 40. Undo: toast with 5s undo action for destructive ops |
| |
| TYPOGRAPHY (Rules 41-55): |
| 41. Body font size: 14-16px, never below 12px |
| 42. Line-height body: 1.6-1.7 |
| 43. Heading line-height: 1.2-1.35 |
| 44. Max line length: 65-75 characters (38-45rem) |
| 45. Min contrast body text: 4.5:1 (WCAG AA) |
| 46. Min contrast large text (18px+): 3:1 |
| 47. Font-weight scale: 400 body, 500 medium, 600 semi-bold, 700 bold |
| 48. Heading scale: H1 40-48px, H2 32px, H3 24px, H4 20px, H5 16px |
| 49. Caption/label: 11-12px, uppercase + tracking for labels |
| 50. Code font: JetBrains Mono, Fira Code, or Roboto Mono |
| 51. Avoid: all-caps for long text, justified text (ragged-right preferred) |
| 52. Letter-spacing: -0.02em for large headings, 0.05em for uppercase labels |
| 53. Orphan control: max 1-2 words on last line |
| 54. RTL support: use logical properties (margin-inline-start not margin-left) |
| 55. Variable fonts preferred for performance |
| |
| PERFORMANCE (Rules 56-65): |
| 56. Lazy load images below fold: loading="lazy" |
| 57. Use CSS transforms for animations (not left/top) |
| 58. will-change: transform only when needed, remove after animation |
| 59. Image formats: WebP preferred, AVIF for hero |
| 60. Icon sprites or SVG inline (avoid icon fonts) |
| 61. Critical CSS inline for above-fold |
| 62. Font display: swap for web fonts |
| 63. Preconnect to font CDNs: <link rel="preconnect"> |
| 64. Debounce resize/scroll handlers |
| 65. Avoid layout thrash: batch DOM reads before writes |
| |
| ACCESSIBILITY (Rules 66-75): |
| 66. All images: meaningful alt text (or alt="" for decorative) |
| 67. All icons used as buttons: aria-label |
| 68. Form inputs: associated <label> (not placeholder as label) |
| 69. Color: never as sole information carrier |
| 70. Tab order: logical DOM order |
| 71. Skip-to-content link: first focusable element |
| 72. ARIA live regions for dynamic content updates |
| 73. Dialog/modal: focus trap + Escape to close |
| 74. Animation: prefers-reduced-motion media query honored |
| 75. Minimum tap target: 44x44px (WCAG 2.5.8) |
| |
| ANTI-PATTERNS (Rules 76-99 — NEVER DO THESE): |
| 76. ✗ Overused AI purple (#9333ea) gradients on white |
| 77. ✗ Rainbow gradients on everything |
| 78. ✗ Emoji as icons in UI (use SVG: Heroicons, Lucide, Phosphor) |
| 79. ✗ Centered ALL-CAPS paragraph text |
| 80. ✗ Hamburger menu on desktop (use sidebar or top nav) |
| 81. ✗ Auto-playing video with sound |
| 82. ✗ Infinite scroll without a way to reach footer |
| 83. ✗ Hover-only interactions (mobile has no hover) |
| 84. ✗ No focus states (keyboard navigation fail) |
| 85. ✗ Text on busy image without overlay/scrim |
| 86. ✗ Placeholder text as the only label |
| 87. ✗ Password field with no show/hide toggle |
| 88. ✗ Generic stock photos of people shaking hands |
| 89. ✗ 3+ font families in one design |
| 90. ✗ Low contrast: gray-on-gray text |
| 91. ✗ Tiny click targets below 36px |
| 92. ✗ Scroll-jacking (overriding native scroll) |
| 93. ✗ Hidden primary navigation on desktop |
| 94. ✗ Modals stacked on modals |
| 95. ✗ Submit button disabled before user interacts with form |
| 96. ✗ Red color for non-error emphasis (trained to mean error) |
| 97. ✗ Underline styling for non-links |
| 98. ✗ Justified text in narrow columns (gaps look bad) |
| 99. ✗ Fixed font sizes that ignore user browser settings (use rem) |
| """ |
|
|
| |
| _CHECKLIST = """ |
| ═══ PRE-DELIVERY CHECKLIST (UI/UX PRO MAX v2.5) ═══ |
| [ ] No placeholder text remaining in UI |
| [ ] All images have meaningful alt text |
| [ ] cursor:pointer on ALL clickable elements |
| [ ] Hover states on all interactive elements (150-300ms) |
| [ ] Focus states visible for keyboard navigation |
| [ ] Responsive tested: 375px / 768px / 1024px / 1440px |
| [ ] Text contrast meets 4.5:1 minimum (WCAG AA) |
| [ ] Loading states for all async operations |
| [ ] Error states handled gracefully |
| [ ] Empty states have helpful message + CTA |
| [ ] prefers-reduced-motion respected in animations |
| [ ] No emoji used as icons (SVG only) |
| [ ] Touch targets minimum 44x44px |
| [ ] Font sizes use rem not px |
| [ ] Form labels associated with inputs |
| [ ] No auto-playing media with sound |
| [ ] Disabled elements have opacity + cursor:not-allowed |
| [ ] Z-index scale documented and consistent |
| [ ] Mobile-first responsive layout verified |
| [ ] No justified text in content columns |
| """ |
|
|
| |
| _STACK_GUIDELINES = { |
| "html-tailwind": """ |
| STACK: HTML + Tailwind CSS |
| • CDN: <script src="https://cdn.tailwindcss.com"></script> |
| • Icons: Heroicons SVG inline or Lucide CDN |
| • Animations: CSS transitions + @keyframes |
| • Fonts: Google Fonts <link> in <head> |
| • JS: Vanilla JS or Alpine.js for interactivity |
| • Component pattern: BEM-like class grouping |
| • Responsive: sm: md: lg: xl: 2xl: prefixes |
| • Dark mode: dark: prefix with class strategy |
| """, |
| "react": """ |
| STACK: React + Tailwind CSS |
| • Setup: Vite + React 18 + tailwindcss |
| • Icons: lucide-react (npm install lucide-react) |
| • Animation: framer-motion |
| • State: useState / useReducer / Zustand |
| • Data fetching: React Query (TanStack) |
| • Forms: React Hook Form + Zod validation |
| • Component pattern: atomic design |
| • Routing: React Router v6 |
| """, |
| "nextjs": """ |
| STACK: Next.js + Tailwind CSS |
| • Version: Next.js 14+ App Router |
| • Icons: lucide-react |
| • Animation: framer-motion |
| • Fonts: next/font/google (no layout shift) |
| • Images: next/image (automatic optimization) |
| • Metadata: export const metadata = { title, description } |
| • Server Components by default, 'use client' for interactivity |
| • API Routes: app/api/route.ts pattern |
| """, |
| "shadcn": """ |
| STACK: shadcn/ui + Next.js + Tailwind |
| • Init: npx shadcn@latest init |
| • Components: npx shadcn@latest add [component] |
| • Theme: CSS variables in globals.css |
| • Icons: lucide-react (included) |
| • Available: Button, Card, Dialog, Input, Select, Table, Toast, etc. |
| • Dark mode: ThemeProvider from next-themes |
| • Typography: cn() utility for conditional classes |
| """, |
| "vue": """ |
| STACK: Vue 3 + Tailwind CSS |
| • Setup: Vite + Vue 3 Composition API |
| • Icons: lucide-vue-next |
| • Animation: @vueuse/motion or vue3-lottie |
| • State: Pinia |
| • Router: Vue Router 4 |
| • Forms: VeeValidate + Zod |
| • Component: <script setup> syntax preferred |
| """, |
| "react-native": """ |
| STACK: React Native + NativeWind |
| • NativeWind: Tailwind for React Native |
| • Icons: @expo/vector-icons or react-native-vector-icons |
| • Navigation: React Navigation v6 |
| • Animation: Reanimated 3 |
| • Min touch target: 44pt (iOS) / 48dp (Android) |
| • Platform-specific: Platform.OS checks |
| • Safe areas: react-native-safe-area-context |
| """, |
| "flutter": """ |
| STACK: Flutter (Dart) |
| • State: Riverpod or Bloc |
| • Navigation: GoRouter |
| • Icons: Material Icons or Phosphor Flutter |
| • Animation: flutter_animate |
| • Min touch: 48dp tap targets |
| • Responsive: LayoutBuilder + MediaQuery |
| • Theme: ThemeData with colorScheme |
| """ |
| } |
|
|
| |
| |
| |
|
|
| def _parse_csv(csv_text: str) -> list[dict]: |
| reader = csv.DictReader(io.StringIO(csv_text.strip())) |
| return list(reader) |
|
|
| def _tokenize(text: str) -> list[str]: |
| return re.findall(r'[a-z0-9]+', text.lower()) |
|
|
| def _bm25_score(query_tokens: list[str], doc_tokens: list[str], |
| avg_dl: float, k1=1.5, b=0.75) -> float: |
| dl = len(doc_tokens) |
| freq = {} |
| for t in doc_tokens: |
| freq[t] = freq.get(t, 0) + 1 |
| score = 0.0 |
| for qt in query_tokens: |
| f = freq.get(qt, 0) |
| if f == 0: |
| continue |
| idf = math.log(1 + (1 / (f + 0.5))) |
| tf = (f * (k1 + 1)) / (f + k1 * (1 - b + b * dl / max(avg_dl, 1))) |
| score += idf * tf |
| return score |
|
|
| class UIUXSearchEngine: |
| """Embedded BM25 search over all UI/UX Pro Max data.""" |
|
|
| def __init__(self): |
| self._styles = _parse_csv(_STYLES_CSV) |
| self._rules = _parse_csv(_RULES_CSV) |
| self._typo = _parse_csv(_TYPOGRAPHY_CSV) |
| self._charts = _parse_csv(_CHARTS_CSV) |
|
|
| |
| def _doc_text(row: dict) -> str: |
| return ' '.join(str(v) for v in row.values()) |
|
|
| self._style_tokens = [_tokenize(_doc_text(r)) for r in self._styles] |
| self._rule_tokens = [_tokenize(_doc_text(r)) for r in self._rules] |
| self._typo_tokens = [_tokenize(_doc_text(r)) for r in self._typo] |
| self._chart_tokens = [_tokenize(_doc_text(r)) for r in self._charts] |
|
|
| self._avg_style = sum(len(t) for t in self._style_tokens) / max(len(self._style_tokens), 1) |
| self._avg_rule = sum(len(t) for t in self._rule_tokens) / max(len(self._rule_tokens), 1) |
| self._avg_typo = sum(len(t) for t in self._typo_tokens) / max(len(self._typo_tokens), 1) |
| self._avg_chart = sum(len(t) for t in self._chart_tokens) / max(len(self._chart_tokens), 1) |
|
|
| def _search(self, query: str, docs: list[dict], tokens: list[list], |
| avg_dl: float, n: int = 3) -> list[dict]: |
| qt = _tokenize(query) |
| scored = [ |
| (i, _bm25_score(qt, tokens[i], avg_dl)) |
| for i in range(len(docs)) |
| ] |
| scored.sort(key=lambda x: x[1], reverse=True) |
| return [docs[i] for i, s in scored[:n] if s > 0] |
|
|
| def search_styles(self, query: str, n: int = 3) -> list[dict]: |
| return self._search(query, self._styles, self._style_tokens, self._avg_style, n) |
|
|
| def search_rules(self, query: str, n: int = 3) -> list[dict]: |
| return self._search(query, self._rules, self._rule_tokens, self._avg_rule, n) |
|
|
| def search_typography(self, query: str, n: int = 3) -> list[dict]: |
| return self._search(query, self._typo, self._typo_tokens, self._avg_typo, n) |
|
|
| def search_charts(self, query: str, n: int = 3) -> list[dict]: |
| return self._search(query, self._charts, self._chart_tokens, self._avg_chart, n) |
|
|
| def generate_design_system(self, query: str, project_name: str = "") -> str: |
| """ |
| Full design system generation — mirrors the Python CLI output. |
| Returns formatted ASCII/Markdown design system block. |
| """ |
| rules = self.search_rules(query, n=1) |
| styles = self.search_styles(query, n=2) |
| typo = self.search_typography(query, n=2) |
| charts = self.search_charts(query, n=2) |
|
|
| rule = rules[0] if rules else {} |
| style = styles[0] if styles else {} |
| font = typo[0] if typo else {} |
|
|
| name = project_name or rule.get('product_type', query.title()) |
|
|
| |
| stack_hint = "" |
| if any(kw in query.lower() for kw in ['react','nextjs','next.js']): |
| stack_hint = _STACK_GUIDELINES.get('react','') |
| elif any(kw in query.lower() for kw in ['html','tailwind']): |
| stack_hint = _STACK_GUIDELINES.get('html-tailwind','') |
| elif any(kw in query.lower() for kw in ['vue','nuxt']): |
| stack_hint = _STACK_GUIDELINES.get('vue','') |
| elif any(kw in query.lower() for kw in ['shadcn','shadcn/ui']): |
| stack_hint = _STACK_GUIDELINES.get('shadcn','') |
|
|
| output = f""" |
| +{'─'*80}+ |
| | TARGET: {name.upper()} — RECOMMENDED DESIGN SYSTEM{' '*(max(0,80-len(name)-41))}| |
| +{'─'*80}+ |
| |
| PATTERN: {rule.get('pattern','Hero-Centric Design')} |
| Conversion: {rule.get('color_mood','Brand-appropriate')} emotion-driven |
| Sections: Hero → Features → Social Proof → CTA → Footer |
| |
| STYLE: {style.get('name', rule.get('style_priority','').split('|')[0])} |
| Keywords: {style.get('keywords','').replace(',', ', ')} |
| Best For: {style.get('best_for','')} |
| Performance: {style.get('performance','Good')} | Accessibility: {style.get('accessibility','WCAG AA')} |
| |
| TYPOGRAPHY: {font.get('pairing', rule.get('typography_mood','Inter / DM Sans'))} |
| Display: {font.get('display_font','')} | Body: {font.get('body_font','')} |
| Mood: {font.get('mood','')} |
| Google Fonts: {font.get('google_fonts_url','')} |
| |
| COLOR MOOD: {rule.get('color_mood','Brand-appropriate palette')} |
| Anti-patterns: {rule.get('anti_patterns','')} |
| |
| KEY EFFECTS: {rule.get('key_effects',style.get('effects','Smooth transitions, hover states'))} |
| |
| AVOID (Anti-patterns): |
| {rule.get('anti_patterns','AI purple gradients on white, rainbow gradients')} |
| |
| PRE-DELIVERY CHECKLIST: |
| [ ] No emojis as icons (use SVG: Heroicons/Lucide) |
| [ ] cursor:pointer on all clickable elements |
| [ ] Hover states with smooth transitions (150-300ms) |
| [ ] Text contrast 4.5:1 minimum |
| [ ] Focus states visible for keyboard navigation |
| [ ] prefers-reduced-motion respected |
| [ ] Responsive: 375px, 768px, 1024px, 1440px |
| [ ] Touch targets minimum 44x44px |
| {f' STACK NOTES:{stack_hint}' if stack_hint else ''} |
| +{'─'*80}+ |
| """ |
| return output.strip() |
|
|
|
|
| |
| _UIUX_ENGINE = UIUXSearchEngine() |
|
|
| def uiux_design_system(query: str, project_name: str = "") -> str: |
| """Public function — call from anywhere in app.py""" |
| return _UIUX_ENGINE.generate_design_system(query, project_name) |
|
|
| def uiux_search(query: str, domain: str = "all", n: int = 3) -> str: |
| """Search UI/UX data by domain: style|rule|typography|chart|all""" |
| results = [] |
| if domain in ("style", "all"): |
| hits = _UIUX_ENGINE.search_styles(query, n) |
| if hits: |
| results.append("**STYLES:**\n" + "\n".join( |
| f"• **{h['name']}** — {h['best_for']}\n Effects: {h['effects']}\n Avoid: {h['avoid']}" |
| for h in hits)) |
| if domain in ("rule", "product", "all"): |
| hits = _UIUX_ENGINE.search_rules(query, n) |
| if hits: |
| results.append("**DESIGN RULES:**\n" + "\n".join( |
| f"• **{h['product_type']}** → {h['pattern']}\n Style: {h['style_priority']}\n Typography: {h['typography_mood']}" |
| for h in hits)) |
| if domain in ("typography", "font", "all"): |
| hits = _UIUX_ENGINE.search_typography(query, n) |
| if hits: |
| results.append("**TYPOGRAPHY:**\n" + "\n".join( |
| f"• **{h['pairing']}** — {h['mood']}\n Best for: {h['best_for']}" |
| for h in hits)) |
| if domain in ("chart", "all"): |
| hits = _UIUX_ENGINE.search_charts(query, n) |
| if hits: |
| results.append("**CHARTS:**\n" + "\n".join( |
| f"• **{h['chart_type']}** — {h['use_case']}\n Library: {h['library']}" |
| for h in hits)) |
| return "\n\n".join(results) if results else "No results found." |
|
|
|
|
| |
| |
| |
|
|
| UIUX_PRO_MAX = f""" |
| ═══ UI/UX PRO MAX v2.5 — DESIGN INTELLIGENCE ═══ |
| Source: nextlevelbuilder/ui-ux-pro-max-skill (68.9k★) |
| |
| DESIGN SYSTEM GENERATION PROCESS: |
| When building any UI or Website, auto-generate design system: |
| 1. Identify product category → match to 161 reasoning rules |
| 2. Select UI style from 67 available styles |
| 3. Choose color palette (industry-appropriate) |
| 4. Pick typography pairing (57 combinations) |
| 5. Apply anti-pattern filters |
| 6. Run pre-delivery checklist |
| |
| ━━━ 67 UI STYLES (key selections) ━━━ |
| |
| MODERN/PREMIUM: |
| • Glassmorphism → SaaS, fintech, AI | frosted glass, backdrop-blur, rgba |
| • Neumorphism → wellness, meditation | soft shadows, embossed, subtle depth |
| • Liquid Glass → premium e-commerce | fluid shapes, layered transparency |
| • Aurora UI → creative agencies | gradient meshes, animated backgrounds |
| • AI-Native UI → chatbots, AI products | dark #0a0a0a, glowing accents, streaming |
| • Soft UI Evolution → enterprise | clean, rounded-2xl, subtle elevation |
| • Neubrutalism → Gen Z brands | bold borders, offset shadows, raw aesthetics |
| |
| CLASSIC/PROFESSIONAL: |
| • Minimalism & Swiss Style → enterprise, docs | strict grid, typography-first |
| • Flat Design → web apps | clean icons, no shadows, bold colors |
| • Dark Mode OLED → coding platforms | pure black #000, minimal battery |
| • Bento Box Grid → dashboards, portfolios | card-based, asymmetric grid |
| |
| CREATIVE/NICHE: |
| • Brutalism → design portfolios | raw, intentionally "broken" layouts |
| • Cyberpunk UI → gaming, crypto | neon on dark, grid lines, glitch effects |
| • Claymorphism → educational, children | 3D clay-like, inflated elements |
| • Ambient Calm UI → meditation, sleep | slow animations, muted palette |
| |
| ━━━ 161 REASONING RULES (product → design) ━━━ |
| AI/Chatbot → AI-Native UI | Dark #0a0a0a | Space Grotesk | Glowing accents |
| SaaS → Glassmorphism or Bento Grid | Neutral | Inter/DM Sans |
| E-commerce → Social Proof | Trust elements | Conversion CTAs above fold |
| Fintech/Crypto → Glassmorphism | Dark + green accent | Roboto Mono for numbers |
| Healthcare → Accessible & Ethical | High contrast | Clean sans-serif | WCAG AA |
| Educational → Claymorphism | Bright primary | Nunito/Quicksand | Friendly |
| Gaming → Cyberpunk | Neon accents | Exo 2/Rajdhani | Dark base |
| Portfolio → Brutalism or Minimalism | Unique typography | Strong visual identity |
| Restaurant → Photo-Centric | Warm palette | Playfair Display | Appetizing imagery |
| Startup MVP → Minimal & Direct | One primary CTA | Fast loading | Sora/DM Sans |
| |
| ━━━ 57 TYPOGRAPHY PAIRINGS ━━━ |
| • Sora + Inter → Modern SaaS, AI products |
| • Playfair Display + Lato → Luxury, restaurant, editorial |
| • Space Grotesk + JetBrains Mono → Tech, coding, crypto |
| • Cormorant Garamond + Montserrat → Premium, legal, architecture |
| • Nunito + Open Sans → Educational, children, wellness |
| • Clash Display + Satoshi → Gen Z, creative agency |
| • DM Serif + DM Sans → Editorial, blog, modern media |
| • Exo 2 + Rajdhani → Gaming, fitness, esports |
| • Libre Baskerville + Source Sans → Legal, finance, B2B |
| • Outfit + DM Sans → App, startup, modern product |
| |
| ━━━ COLOR PALETTES ━━━ |
| DARK THEMES: |
| • AI Dark: bg #0a0a0a surface #141414 accent #e11d48 |
| • Cyber: bg #0d0d1a surface #1a1a2e accent #00fff5 |
| • Pro Dark: bg #0f0f0f surface #1a1a1a accent #6366f1 |
| |
| LIGHT THEMES: |
| • Clean SaaS: bg #ffffff surface #f8fafc accent #0ea5e9 |
| • Warm: bg #fafaf9 surface #f5f5f4 accent #f59e0b |
| • Premium: bg #fafafa surface #ffffff accent #8b5cf6 |
| |
| ━━━ 25 CHART TYPES ━━━ |
| Line/Area → trends | Bar/Stacked → comparison | Donut → KPI proportion |
| Candlestick → financial | Funnel → conversion | Gauge → single metric |
| Heatmap → matrix density | Treemap → hierarchy | Radar → multi-dimension |
| Sankey → flow | Choropleth → geographic | Gantt → project timeline |
| |
| {_UX_GUIDELINES} |
| |
| {_CHECKLIST} |
| |
| ━━━ PRE-DELIVERY CHECKLIST ━━━ |
| [ ] No placeholder text remaining |
| [ ] cursor:pointer on ALL clickables |
| [ ] Hover states (150-300ms transitions) |
| [ ] Focus states visible |
| [ ] Responsive: 375/768/1024/1440px |
| [ ] Text contrast 4.5:1 minimum |
| [ ] Loading + error + empty states |
| [ ] prefers-reduced-motion respected |
| [ ] No emoji as icons (SVG only) |
| [ ] Touch targets 44x44px minimum |
| """ |
|
|
| |
| |
| |
| class ChatRequest(BaseModel): |
| message:str; session_id:Optional[str]=None |
| task_type:Optional[str]=None; mode:Optional[str]=None |
|
|
| class ExamRequest(BaseModel): |
| subject:str; class_:str; topic:Optional[str]=None |
| q_count:Optional[int]=10; type_:Optional[str]="mixed"; lang:Optional[str]="bn" |
|
|
| |
| |
| |
| NCTB_2026 = { |
| "Pre_Primary": { |
| "books": ["আমার বই","এসো লিখতে শিখি"], |
| "note": "শিশুর প্রাক-প্রাথমিক শিক্ষার ভিত্তি গড়ে তোলার জন্য" |
| }, |
| "Class_1": { |
| "subjects": { |
| "আমার বাংলা বই": "বাংলা ভাষা শিক্ষা, বর্ণমালা, ছড়া, গল্প", |
| "English for Today": "Basic English, alphabets, simple sentences", |
| "আনন্দ পাঠ (বাংলা সহপাঠ)": "বাংলা সহায়ক পাঠ", |
| "প্রাথমিক গণিত": "সংখ্যা, যোগ, বিয়োগ, আকার", |
| } |
| }, |
| "Class_2": { |
| "subjects": { |
| "আমার বাংলা বই": "বাংলা ব্যাকরণ, রচনা, কবিতা", |
| "English for Today": "Reading, writing, grammar basics", |
| "প্রাথমিক গণিত": "গুণ, ভাগ, পরিমাপ", |
| } |
| }, |
| "Class_3": { |
| "subjects": { |
| "আমার বাংলা বই": "বাংলা সাহিত্য, ব্যাকরণ, রচনা", |
| "English for Today": "Reading comprehension, vocabulary, writing", |
| "প্রাথমিক গণিত": "জ্যামিতি, ভগ্নাংশ, সমস্যা সমাধান", |
| "প্রাথমিক বিজ্ঞান": "পরিবেশ, উদ্ভিদ, প্রাণী, আবহাওয়া", |
| "বাংলাদেশ ও বিশ্বপরিচয়": "বাংলাদেশের ইতিহাস, ভূগোল, সংস্কৃতি", |
| "ইসলাম ও নৈতিক শিক্ষা": "ইসলামিক মূল্যবোধ ও আখলাক", |
| "হিন্দুধর্ম ও নৈতিক শিক্ষা": "হিন্দু ধর্মীয় মূল্যবোধ", |
| } |
| }, |
| "Class_4": { |
| "subjects": { |
| "আমার বাংলা বই": "বাংলা সাহিত্য ও ব্যাকরণ উন্নত", |
| "English for Today": "Advanced reading, grammar, composition", |
| "প্রাথমিক গণিত": "দশমিক, শতকরা, পরিমাপ", |
| "প্রাথমিক বিজ্ঞান": "মানবদেহ, পদার্থ, শক্তি", |
| "বাংলাদেশ ও বিশ্বপরিচয়": "মুক্তিযুদ্ধ, সংবিধান, বিশ্ব", |
| "ইসলাম ও নৈতিক শিক্ষা": "নামাজ, কোরান, হাদিস", |
| } |
| }, |
| "Class_5": { |
| "subjects": { |
| "আমার বাংলা বই": "উন্নত সাহিত্য ও রচনা", |
| "English for Today": "Essay writing, advanced grammar", |
| "প্রাথমিক গণিত": "বীজগণিত পরিচিতি, পরিমিতি", |
| "প্রাথমিক বিজ্ঞান": "রাসায়নিক পরিবর্তন, জীববিজ্ঞান", |
| "বাংলাদেশ ও বিশ্বপরিচয়": "অর্থনীতি, বৈশ্বিক সম্পর্ক", |
| "ইসলাম ও নৈতিক শিক্ষা": "সিরাত, ফিকহ", |
| } |
| }, |
| "Class_6": { |
| "level": "JSC/Junior Secondary", |
| "subjects": { |
| "বাংলা": "বাংলা সাহিত্য: কবিতা, গল্প, উপন্যাস, প্রবন্ধ; ব্যাকরণ: ধ্বনি, শব্দ, বাক্য, সমাস, কারক", |
| "English": "Reading comprehension, essay writing, grammar, letter writing", |
| "গণিত": "বাস্তব সংখ্যা, সরল সমীকরণ, জ্যামিতি, পরিমাপ", |
| "বিজ্ঞান": "পদার্থ, শক্তি, জীববিজ্ঞান, রসায়নের প্রাথমিক ধারণা", |
| "সামাজিক বিজ্ঞান": "ইতিহাস, ভূগোল, অর্থনীতি, নাগরিক শিক্ষা", |
| "তথ্য ও যোগাযোগ প্রযুক্তি (ICT)": "কম্পিউটার, ইন্টারনেট, প্রোগ্রামিং পরিচিতি", |
| "ইসলাম ও নৈতিক শিক্ষা": "আকিদা, ইবাদত, আখলাক, মুয়ামালাত", |
| "কৃষি শিক্ষা": "কৃষি ও খাদ্য উৎপাদন", |
| "শারীরিক শিক্ষা ও স্বাস্থ্য": "স্বাস্থ্যবিধি, ব্যায়াম, খেলাধুলা", |
| "চারু ও কারুকলা": "শিল্পকলা, ডিজাইন, রং", |
| "জীবন ও জীবিকা": "পেশা পরিচিতি, জীবন দক্ষতা (নতুন পাঠ্যক্রম ২০২৬)", |
| "ডিজিটাল প্রযুক্তি": "ডিজিটাল লিটারেসি, কোডিং (নতুন পাঠ্যক্রম ২০২৬)", |
| "সুস্থতা ও সমৃদ্ধ জীবন": "ওয়েলনেস, মানসিক স্বাস্থ্য (নতুন পাঠ্যক্রম ২০২৬)", |
| } |
| }, |
| "Class_7": { |
| "level": "JSC/Junior Secondary", |
| "subjects": { |
| "বাংলা": "উন্নত সাহিত্য বিশ্লেষণ, রচনা, ব্যাকরণ", |
| "English": "Advanced grammar, literature, formal writing", |
| "গণিত": "বীজগণিত, জ্যামিতি, পরিসংখ্যান প্রাথমিক", |
| "বিজ্ঞান": "পদার্থবিজ্ঞান, রসায়ন, জীববিজ্ঞান, ভূগোল", |
| "সামাজিক বিজ্ঞান": "বাংলাদেশের ইতিহাস, বিশ্ব ইতিহাস, ভূগোল", |
| "তথ্য ও যোগাযোগ প্রযুক্তি (ICT)": "স্প্রেডশিট, উপস্থাপনা, ওয়েব পেজ", |
| "ইসলাম ও নৈতিক শিক্ষা": "কোরান তাফসির, হাদিস, ফিকহ", |
| "কৃষি শিক্ষা": "আধুনিক কৃষি, মৎস্য ও প্রাণিসম্পদ", |
| "জীবন ও জীবিকা": "উদ্যোক্তা, ক্যারিয়ার পরিকল্পনা", |
| "ডিজিটাল প্রযুক্তি": "প্রোগ্রামিং, সাইবার নিরাপত্তা", |
| } |
| }, |
| "Class_8": { |
| "level": "JSC", |
| "subjects": { |
| "বাংলা": "সাহিত্য সমালোচনা, ব্যাকরণের গভীর জ্ঞান, সৃজনশীল রচনা", |
| "English": "Advanced literature, composition, communication skills", |
| "গণিত": "দ্বিঘাত সমীকরণ, ত্রিকোণমিতি পরিচিতি, পরিসংখ্যান", |
| "সাধারণ বিজ্ঞান": "পদার্থ, রসায়ন, জীববিজ্ঞান, পরিবেশ বিজ্ঞান", |
| "বাংলাদেশ ও বিশ্বপরিচয়": "জাতীয় ইতিহাস, বিশ্ব রাজনীতি", |
| "তথ্য ও যোগাযোগ প্রযুক্তি": "ডেটাবেস, নেটওয়ার্কিং, ওয়েব ডিজাইন", |
| "ইসলাম ও নৈতিক শিক্ষা": "শরিয়া, সিরাত, ইসলামি সভ্যতা", |
| } |
| }, |
| "SSC_Class_9_10": { |
| "level": "SSC (Secondary School Certificate)", |
| "exam_board": "Bangladesh Board SSC Exam", |
| "groups": { |
| "বিজ্ঞান (Science)": { |
| "mandatory": ["বাংলা","English","গণিত","তথ্য ও যোগাযোগ প্রযুক্তি"], |
| "elective": ["পদার্থবিজ্ঞান","রসায়ন","জীববিজ্ঞান","উচ্চতর গণিত"], |
| "optional": ["কৃষি শিক্ষা","গার্হস্থ্য বিজ্ঞান","বাংলাদেশ ও বিশ্বপরিচয়"], |
| }, |
| "মানবিক (Arts)": { |
| "mandatory": ["বাংলা","English","গণিত","তথ্য ও যোগাযোগ প্রযুক্তি"], |
| "elective": ["ইতিহাস","ভূগোল ও পরিবেশ","পৌরনীতি ও নাগরিকতা","অর্থনীতি"], |
| "optional": ["বাংলাদেশ ও বিশ্বপরিচয়"], |
| }, |
| "ব্যবসায় শিক্ষা (Commerce)": { |
| "mandatory": ["বাংলা","English","গণিত","তথ্য ও যোগাযোগ প্রযুক্তি"], |
| "elective": ["হিসাববিজ্ঞান","ব্যবসায় উদ্যোগ","ফিন্যান্স ও ব্যাংকিং"], |
| "optional": ["অর্থনীতি","বাংলাদেশ ও বিশ্বপরিচয়"], |
| } |
| }, |
| "subject_details": { |
| "পদার্থবিজ্ঞান": "গতি, নিউটনের সূত্র, কাজ-শক্তি, তরঙ্গ, আলো, তড়িৎ, আধুনিক পদার্থবিজ্ঞান", |
| "রসায়ন": "পরমাণু গঠন, পর্যায় সারণি, রাসায়নিক বন্ধন, জৈব রসায়ন, পরীক্ষা-নিরীক্ষা", |
| "জীববিজ্ঞান": "কোষ, টিস্যু, উদ্ভিদ, প্রাণী, মানবদেহ, বংশগতি, বিবর্তন", |
| "উচ্চতর গণিত": "ত্রিকোণমিতি, ক্যালকুলাস পরিচিতি, ভেক্টর, পরিসংখ্যান", |
| "হিসাববিজ্ঞান": "ডেবিট-ক্রেডিট, জার্নাল, লেজার, ট্রায়াল ব্যালেন্স, আর্থিক বিবরণী", |
| "ব্যবসায় উদ্যোগ": "উদ্যোক্তা, ব্যবসায় পরিকল্পনা, বিপণন, ব্যবস্থাপনা", |
| "ইতিহাস": "প্রাচীন বাংলা, মধ্যযুগ, মুঘল, ব্রিটিশ, বাংলাদেশের স্বাধীনতা", |
| "ভূগোল ও পরিবেশ": "পৃথিবীর গঠন, জলবায়ু, বাংলাদেশের ভূগোল, পরিবেশ সংরক্ষণ", |
| } |
| }, |
| "HSC_Class_11_12": { |
| "level": "HSC (Higher Secondary Certificate)", |
| "exam_board": "Bangladesh Board HSC Exam", |
| "groups": { |
| "বিজ্ঞান (Science)": { |
| "mandatory": ["বাংলা","English","তথ্য ও যোগাযোগ প্রযুক্তি"], |
| "elective_1st": ["পদার্থবিজ্ঞান ১ম ও ২য় পত্র"], |
| "elective_2nd": ["রসায়ন ১ম ও ২য় পত্র"], |
| "elective_3rd": ["জীববিজ্ঞান / উচ্চতর গণিত"], |
| }, |
| "মানবিক (Arts)": { |
| "mandatory": ["বাংলা","English","তথ্য ও যোগাযোগ প্রযুক্তি"], |
| "elective": ["ইতিহাস","ইসলামের ইতিহাস","ভূগোল","পৌরনীতি","সমাজবিজ্ঞান","অর্থনীতি","দর্শন","মনোবিজ্ঞান"], |
| }, |
| "ব্যবসায় শিক্ষা (Commerce)": { |
| "mandatory": ["বাংলা","English","তথ্য ও যোগাযোগ প্রযুক্তি"], |
| "elective": ["হিসাববিজ্ঞান ১ম ও ২য় পত্র","ব্যবসায় সংগঠন ও ব্যবস্থাপনা","ফিন্যান্স, ব্যাংকিং ও বিমা","উৎপাদন ব্যবস্থাপনা ও বিপণন"], |
| } |
| }, |
| "subject_details": { |
| "পদার্থবিজ্ঞান": "ভেক্টর, নিউটনীয় বলবিদ্যা, কাজ-শক্তি-ক্ষমতা, তরঙ্গ, তাপগতিবিদ্যা, আলোকবিজ্ঞান, তড়িচ্চুম্বকতা, আধুনিক পদার্থবিজ্ঞান (কোয়ান্টাম, পারমাণবিক)", |
| "রসায়ন": "পরমাণু মডেল, রাসায়নিক সাম্য, তড়িৎ রসায়ন, জৈব যৌগ, পলিমার, পরিবেশ রসায়ন", |
| "জীববিজ্ঞান": "কোষবিভাজন, বংশগতি ও বিবর্তন, উদ্ভিদ শারীরতত্ত্ব, প্রাণী শারীরবিজ্ঞান, মানব স্বাস্থ্য", |
| "উচ্চতর গণিত": "ম্যাট্রিক্স, ভেক্টর, ক্যালকুলাস (অবকল, সমাকল), জটিল সংখ্যা, বিন্যাস-সমাবেশ, সম্ভাবনা", |
| "হিসাববিজ্ঞান": "অংশীদারি ব্যবসা, কোম্পানি হিসাব, মূলধন বাজেটিং, আর্থিক বিশ্লেষণ", |
| "অর্থনীতি": "ব্যষ্টিক অর্থনীতি, সামষ্টিক অর্থনীতি, বাংলাদেশের অর্থনীতি, আন্তর্জাতিক বাণিজ্য", |
| "ICT": "ডেটা স্ট্রাকচার, অ্যালগরিদম, ডেটাবেস, নেটওয়ার্ক, ওয়েব ডেভেলপমেন্ট, সাইবার নিরাপত্তা", |
| } |
| } |
| } |
|
|
| NCTB_CONTEXT = """ |
| [NCTB 2026 — Bangladesh National Curriculum & Textbook Board] |
| তুমি বাংলাদেশের ২০২৬ সালের সম্পূর্ণ NCTB পাঠ্যক্রম জানো: |
| |
| প্রাথমিক (Class 1-5): বাংলা, ইংরেজি, গণিত, বিজ্ঞান, বাংলাদেশ ও বিশ্বপরিচয়, ধর্ম শিক্ষা |
| মাধ্যমিক (Class 6-8): উপরের + ICT, কৃষি, শারীরিক শিক্ষা + নতুন বিষয়: জীবন ও জীবিকা, ডিজিটাল প্রযুক্তি |
| SSC (Class 9-10): বিজ্ঞান/মানবিক/ব্যবসায় শিক্ষা গ্রুপ |
| HSC (Class 11-12): উন্নত বিজ্ঞান/মানবিক/ব্যবসায় গ্রুপ |
| |
| নতুন পাঠ্যক্রম ২০২৬ বৈশিষ্ট্য: |
| - Competency-based learning (মুখস্থ নয়, দক্ষতা অর্জন) |
| - সৃজনশীল প্রশ্নপদ্ধতি (Creative Question) |
| - Digital Technology বিষয় নতুন যুক্ত |
| - জীবন ও জীবিকা বিষয় নতুন |
| - সুস্থতা ও সমৃদ্ধ জীবন বিষয় নতুন |
| """ |
|
|
| |
| |
| |
| |
| GAME_DEV_STUDIO = """ |
| ═══ GAME DEV STUDIO INTELLIGENCE ═══ |
| |
| STUDIO HIERARCHY (when building games): |
| Tier 1 — Directors: creative-director, technical-director, producer |
| Tier 2 — Leads: game-designer, lead-programmer, art-director, audio-director, qa-lead |
| Tier 3 — Specialists: gameplay-programmer, ui-programmer, level-designer, systems-designer |
| |
| ENGINE SELECTION RULES: |
| - Godot 4 → indie, 2D/3D, open source, GDScript/C#, lightweight |
| - Unity → mobile, cross-platform, C#, large asset store |
| - Unreal 5 → AAA quality, C++/Blueprints, photorealistic graphics |
| |
| GAME DESIGN FRAMEWORKS: |
| - MDA Framework → Mechanics (rules) → Dynamics (behavior) → Aesthetics (experience) |
| - Flow State Design → keep challenge slightly above skill level |
| - Bartle Types → Achievers / Explorers / Socializers / Killers |
| - Self-Determination Theory → Autonomy + Competence + Relatedness |
| |
| GAME ARCHITECTURE RULES: |
| - Data-driven values — NO hardcoded magic numbers (use config files) |
| - Delta time always — movement *= delta_time (never frame-rate dependent) |
| - Server-authoritative for multiplayer — never trust client |
| - Component-based design — prefer composition over inheritance |
| - Object pooling for bullets/particles (never Instantiate in hot loops) |
| - State machines for game logic (Player: Idle/Run/Jump/Attack/Dead) |
| |
| GAME GENRE → TECH STACK: |
| - 2D Platformer → Godot 4 (CharacterBody2D, TileMap, AnimationPlayer) |
| - 3D Action → Unreal 5 (GAS, Blueprints) or Unity (DOTS for performance) |
| - Mobile Casual → Unity (lightweight, IL2CPP, addressables) |
| - Puzzle → Godot 4 or Unity (simple, fast iteration) |
| - Multiplayer → Unity Netcode or Unreal Replication or Godot MultiplayerAPI |
| - RPG → Godot 4 (Resource system perfect for items/stats) or Unreal (GAS) |
| |
| CODE QUALITY GATES: |
| ✓ No magic numbers — extract to constants/config |
| ✓ Delta time on all movement/physics |
| ✓ Error handling on save/load |
| ✓ Input abstraction layer (not direct key checks in game logic) |
| ✓ Separate game logic from rendering |
| ✓ Unit tests for game systems (economy, combat math) |
| |
| GAME DOCUMENT TEMPLATES: |
| GDD must have: Vision, Core Loop, Mechanics, Progression, Economy, Art Style, Audio, Scope |
| Sprint plan: Goal, Tasks, Acceptance Criteria, Dependencies, Risk |
| |
| WHEN USER ASKS TO BUILD A GAME: |
| 1. Ask: genre? platform? engine preference? team size? scope? |
| 2. Suggest engine based on answers |
| 3. Design core loop first (what does player DO every 30 seconds?) |
| 4. Define win/lose conditions |
| 5. Minimal viable prototype first — one mechanic, working end-to-end |
| 6. Then expand: UI, audio, polish, menus |
| """ |
| |
| |
| |
| BN_UNICODE = re.compile(r'[\u0980-\u09FF]') |
|
|
| |
| BN_ROMAN_WORDS = [ |
| 'ami','tumi','apni','ki','keno','kothay','ache','hobe','koro','bolo','jano', |
| 'likhte','bolte','dite','nao','dao','jao','aso','thako','dekho','eta','ota', |
| 'eita','oita','emon','onek','ektu','bhalo','kharap','shundor','kothin','shohoj', |
| 'porashona','bishoy','bujhi','bujhte','bujhao','korao','shekao','korte','korbo', |
| 'hoyeche','lagbe','chai','thakbe','parbo','bolo','janao','dekhai','shekhai', |
| 'acche','nai','hoy','theke','diye','niye','boro','choto','valo','tomar','amar', |
| 'apnar','amader','tomader','apnader','kintu','tahole','karon','jodi','nahole', |
| 'ekhon','pore','age','kal','aaj','raat','din','shomoy','khabar','pani','basha', |
| 'kaj','por','lekho','poro','dekho','shono','bujho','jao','esho','thako', |
| ] |
|
|
| def detect_lang(text: str) -> str: |
| """ |
| Detect language: |
| - "bn" → Bengali Unicode characters found |
| - "en" → English only, no Bangla signals |
| - "bn_roman" → Romanized Bangla words detected (Banglish) |
| """ |
| |
| if BN_UNICODE.search(text): |
| return "bn" |
|
|
| lower = text.lower() |
| words = re.findall(r'\b[a-z]+\b', lower) |
| if not words: |
| return "en" |
|
|
| |
| bn_count = sum(1 for w in words if w in BN_ROMAN_WORDS) |
| total_words = len(words) |
|
|
| |
| if bn_count >= 2 and (bn_count / total_words) >= 0.25: |
| return "bn_roman" |
|
|
| return "en" |
|
|
|
|
| def lang_instr(lang: str) -> str: |
| if lang == "bn": |
| return ( |
| "⚡ CRITICAL LANGUAGE RULE: The user is writing in Bengali (বাংলা).\n" |
| "You MUST reply ENTIRELY in Bengali script (বাংলা হরফে).\n" |
| "Every single word must be in Bengali. No English words allowed in the reply.\n" |
| "Example: 'হ্যাঁ, এটা একটি গুরুত্বপূর্ণ বিষয়। চলো ধাপে ধাপে বুঝি...'" |
| ) |
| if lang == "bn_roman": |
| return ( |
| "⚡ CRITICAL LANGUAGE RULE: The user is writing in Banglish (Romanized Bengali + English mix).\n" |
| "You MUST reply in PURE ENGLISH only — clear, simple, friendly English.\n" |
| "Do NOT use Bengali script. Do NOT mix languages in your reply.\n" |
| "Reason: The user is typing Bangla words in English letters, " |
| "so they can read English comfortably. Give a clean English answer.\n" |
| "Example: 'Yes, this is an important concept. Let me explain step by step...'" |
| ) |
| return "" |
| |
| |
| |
| def _db(): return sqlite3.connect(str(DB_PATH),check_same_thread=False) |
| def init_db(): |
| with _db() as c: |
| c.executescript(""" |
| CREATE TABLE IF NOT EXISTS messages(id INTEGER PRIMARY KEY AUTOINCREMENT, |
| session_id TEXT,role TEXT,content TEXT,ts REAL DEFAULT(unixepoch('now'))); |
| CREATE TABLE IF NOT EXISTS sessions(id TEXT PRIMARY KEY,title TEXT,mode TEXT, |
| updated REAL DEFAULT(unixepoch('now'))); |
| CREATE TABLE IF NOT EXISTS rag_docs(id INTEGER PRIMARY KEY AUTOINCREMENT, |
| doc_id TEXT,title TEXT,chunk TEXT,source TEXT,category TEXT DEFAULT 'general', |
| ts REAL DEFAULT(unixepoch('now'))); |
| CREATE TABLE IF NOT EXISTS live_feed(id INTEGER PRIMARY KEY AUTOINCREMENT, |
| title TEXT,summary TEXT,url TEXT,source TEXT,category TEXT, |
| published TEXT,fetched_at REAL DEFAULT(unixepoch('now'))); |
| CREATE TABLE IF NOT EXISTS knowledge_log(id INTEGER PRIMARY KEY AUTOINCREMENT, |
| source TEXT,items_fetched INTEGER,ts REAL DEFAULT(unixepoch('now'))); |
| CREATE INDEX IF NOT EXISTS idx_msg ON messages(session_id); |
| CREATE INDEX IF NOT EXISTS idx_feed ON live_feed(category,fetched_at); |
| """) |
| init_db() |
|
|
| def mem_add(sid,role,content): |
| with _db() as c: |
| c.execute("INSERT INTO messages(session_id,role,content) VALUES(?,?,?)",(sid,role,content[:15000])) |
| c.execute("INSERT OR REPLACE INTO sessions(id,title,updated) VALUES(?,?,unixepoch('now'))", |
| (sid,content[:55] if role=="user" else None)) |
|
|
| def mem_get(sid,limit=16): |
| with _db() as c: |
| rows=c.execute("SELECT role,content FROM messages WHERE session_id=? ORDER BY ts DESC LIMIT ?",(sid,limit)).fetchall() |
| return [{"role":r,"content":cn} for r,cn in reversed(rows)] |
|
|
| def mem_sessions(): |
| with _db() as c: |
| rows=c.execute("SELECT id,title,mode,updated FROM sessions ORDER BY updated DESC LIMIT 40").fetchall() |
| return [{"id":r[0],"title":r[1] or "Conversation","mode":r[2],"updated":r[3]} for r in rows] |
|
|
| def mem_delete(sid): |
| with _db() as c: |
| c.execute("DELETE FROM messages WHERE session_id=?",(sid,)) |
| c.execute("DELETE FROM sessions WHERE id=?",(sid,)) |
|
|
| def mem_stats(): |
| with _db() as c: |
| msgs=c.execute("SELECT COUNT(*) FROM messages").fetchone()[0] |
| sess=c.execute("SELECT COUNT(*) FROM sessions").fetchone()[0] |
| feeds=c.execute("SELECT COUNT(*) FROM live_feed").fetchone()[0] |
| last=c.execute("SELECT MAX(fetched_at) FROM knowledge_log").fetchone()[0] |
| return {"messages":msgs,"sessions":sess,"live_articles":feeds, |
| "last_update":datetime.fromtimestamp(last,timezone.utc).strftime("%Y-%m-%d %H:%M UTC") if last else "Not yet"} |
|
|
| def rag_store(title,text,source,category="general"): |
| doc_id=hashlib.md5((title+text[:60]).encode()).hexdigest()[:10] |
| words=text.split() |
| with _db() as c: |
| c.execute("DELETE FROM rag_docs WHERE doc_id=?",(doc_id,)) |
| for i in range(0,len(words),300): |
| chunk=" ".join(words[i:i+380]) |
| if len(chunk.strip())>40: |
| c.execute("INSERT INTO rag_docs(doc_id,title,chunk,source,category) VALUES(?,?,?,?,?)",(doc_id,title,chunk,source,category)) |
|
|
| def rag_search(query,limit=4,category=None): |
| words=set(re.findall(r"\w{4,}",query.lower())) |
| if not words: return [] |
| with _db() as c: |
| if category: |
| rows=c.execute("SELECT title,chunk,source FROM rag_docs WHERE category=? ORDER BY ts DESC LIMIT 400",(category,)).fetchall() |
| else: |
| rows=c.execute("SELECT title,chunk,source FROM rag_docs ORDER BY ts DESC LIMIT 400").fetchall() |
| scored=[(len(words&set(re.findall(r"\w{4,}",ch.lower())))/max(len(words),1),t,ch[:300],s) for t,ch,s in rows] |
| return sorted([x for x in scored if x[0]>0.10],reverse=True)[:limit] |
|
|
| def feed_store(title,summary,url,source,category,published=""): |
| with _db() as c: |
| exists=c.execute("SELECT id FROM live_feed WHERE url=?",(url,)).fetchone() |
| if not exists: |
| c.execute("INSERT INTO live_feed(title,summary,url,source,category,published) VALUES(?,?,?,?,?,?)", |
| (title,summary[:500],url,source,category,published)) |
| rag_store(title,f"{title}. {summary}",source,category) |
|
|
| def feed_get(category=None,limit=8): |
| with _db() as c: |
| if category: |
| rows=c.execute("SELECT title,summary,url,source,published FROM live_feed WHERE category=? ORDER BY fetched_at DESC LIMIT ?",(category,limit)).fetchall() |
| else: |
| rows=c.execute("SELECT title,summary,url,source,published FROM live_feed ORDER BY fetched_at DESC LIMIT ?",(limit,)).fetchall() |
| return [{"title":r[0],"summary":r[1],"url":r[2],"source":r[3],"published":r[4]} for r in rows] |
|
|
| |
| |
| |
| def _get(url,params=None,timeout=8): |
| try: |
| headers={"User-Agent":"RUBRA/8.0"} |
| return _req.get(url,params=params,headers=headers,timeout=timeout) |
| except: return None |
|
|
| CITY_COORDS={"dhaka":(23.81,90.41),"london":(51.51,-0.13),"new york":(40.71,-74.01), |
| "tokyo":(35.68,139.65),"paris":(48.86,2.35),"sydney":(-33.87,151.21),"dubai":(25.20,55.27), |
| "singapore":(1.35,103.82),"berlin":(52.52,13.41),"mumbai":(19.08,72.88),"beijing":(39.90,116.41), |
| "seoul":(37.57,126.98),"chittagong":(22.33,91.84),"sylhet":(24.89,91.87),"rajshahi":(24.37,88.60), |
| "delhi":(28.61,77.21),"karachi":(24.86,67.01),"chicago":(41.88,-87.63),"toronto":(43.65,-79.38), |
| "istanbul":(41.01,28.98),"cairo":(30.04,31.24),"lagos":(6.52,3.38),"jakarta":(-6.21,106.85)} |
| WC={0:"☀️ Clear",1:"🌤 Clear",2:"⛅ Partly cloudy",3:"☁️ Overcast",45:"🌫 Foggy", |
| 51:"🌦 Drizzle",61:"🌧 Light rain",63:"🌧 Rain",65:"🌧 Heavy rain",71:"❄️ Snow", |
| 80:"🌦 Showers",95:"⛈ Thunderstorm"} |
|
|
| def tool_weather(city="Dhaka"): |
| lat,lon=CITY_COORDS.get(city.lower().strip(),(23.81,90.41)) |
| r=_get("https://api.open-meteo.com/v1/forecast",{"latitude":lat,"longitude":lon,"timezone":"auto", |
| "current":["temperature_2m","relative_humidity_2m","wind_speed_10m","weather_code","apparent_temperature","precipitation"]}) |
| if not r: return None |
| try: |
| curr=r.json()["current"] |
| return {"city":city.title(),"temp":curr.get("temperature_2m"),"feels":curr.get("apparent_temperature"), |
| "humidity":curr.get("relative_humidity_2m"),"wind":curr.get("wind_speed_10m"), |
| "precip":curr.get("precipitation",0),"condition":WC.get(curr.get("weather_code",0),"Unknown")} |
| except: return None |
|
|
| def tool_crypto(coins="bitcoin,ethereum,solana"): |
| r=_get("https://api.coingecko.com/api/v3/simple/price",{"ids":coins,"vs_currencies":"usd","include_24hr_change":"true"}) |
| if not r: return None |
| try: return r.json() |
| except: return None |
|
|
| def tool_currency(base="USD"): |
| r=_get("https://api.frankfurter.app/latest",{"from":base,"to":"EUR,GBP,JPY,BDT,INR,CAD,AUD,CNY,SGD"}) |
| if not r: return None |
| try: d=r.json(); return {"base":base,"rates":d.get("rates",{}),"date":d.get("date","")} |
| except: return None |
|
|
| def tool_wikipedia(query,sentences=7): |
| r=_get("https://en.wikipedia.org/w/api.php",{"action":"query","format":"json","prop":"extracts", |
| "exsentences":sentences,"exintro":True,"explaintext":True,"redirects":1,"titles":query}) |
| if not r: return None |
| try: |
| pages=r.json()["query"]["pages"]; page=next(iter(pages.values())) |
| if "extract" in page and len(page["extract"])>80: |
| rag_store(page.get("title",""),page["extract"],"wikipedia","general") |
| return {"title":page.get("title",""),"text":page["extract"][:2000]} |
| except: pass |
| return None |
|
|
| def tool_arxiv(query,n=3): |
| r=_get("https://export.arxiv.org/api/query",{"search_query":f"all:{query}","max_results":n},timeout=12) |
| if not r: return [] |
| try: |
| root=ET.fromstring(r.content); ns={"a":"http://www.w3.org/2005/Atom"}; out=[] |
| for e in root.findall("a:entry",ns): |
| t=e.find("a:title",ns).text.strip().replace("\n"," ") |
| s=e.find("a:summary",ns).text.strip()[:400] |
| l=e.find("a:id",ns).text.strip() |
| a=[x.find("a:name",ns).text for x in e.findall("a:author",ns)[:2]] |
| out.append({"title":t,"summary":s,"link":l,"authors":a}); rag_store(t,s,"arxiv","ai") |
| return out |
| except: return [] |
|
|
| def tool_books(query,n=5): |
| r=_get("https://openlibrary.org/search.json",{"q":query,"limit":n,"fields":"title,author_name,first_publish_year"}) |
| if not r: return [] |
| try: return [{"title":d.get("title",""),"authors":d.get("author_name",[])[:2],"year":d.get("first_publish_year","")} for d in r.json().get("docs",[])[:n]] |
| except: return [] |
|
|
| def tool_books_2026(query=""): |
| results=[] |
| |
| r=_get("https://www.googleapis.com/books/v1/volumes", |
| {"q":f"{query or 'bestseller'} 2025 2026","orderBy":"newest","maxResults":10,"langRestrict":"en"}) |
| if r: |
| try: |
| for item in r.json().get("items",[]): |
| info=item.get("volumeInfo",{}); pub=info.get("publishedDate","") |
| year=int(pub[:4]) if pub and len(pub)>=4 and pub[:4].isdigit() else 0 |
| if year>=2023: |
| results.append({"title":info.get("title",""),"authors":info.get("authors",[])[:2],"year":year,"subjects":info.get("categories",[])[:2]}) |
| except: pass |
| |
| if not results: |
| r=_get("https://openlibrary.org/search.json",{"q":query or "fiction 2025","sort":"new","limit":8,"fields":"title,author_name,first_publish_year"}) |
| if r: |
| try: |
| books=r.json().get("docs",[]); results=[b for b in books if b.get("first_publish_year",0)>=2023] |
| except: pass |
| seen=set(); unique=[] |
| for b in results: |
| t=b.get("title","").lower()[:40] |
| if t and t not in seen: seen.add(t); unique.append(b) |
| return unique[:6] |
|
|
| def tool_calc(expr): |
| try: |
| clean=re.sub(r"[^0-9+\-*/().\s%]","",expr.replace("^","**").replace("×","*").replace("÷","/")) |
| result=eval(clean,{"__builtins__":{},"math":math,"abs":abs,"round":round,"sqrt":math.sqrt, |
| "sin":math.sin,"cos":math.cos,"tan":math.tan,"log":math.log,"pi":math.pi,"e":math.e}) |
| return {"expr":expr,"result":result} |
| except Exception as ex: return {"error":str(ex)} |
| |
| |
| |
|
|
| |
| _UNSAFE_DOMAINS = { |
| 'onion', 'tor2web', 'i2p', 'bit.ly', 'tinyurl.com', |
| 'pastebin.com', 'rentry.co', 'ghostbin.com', |
| } |
|
|
| |
| _SAFE_CONTENT = {'text/html', 'text/plain', 'application/xhtml+xml', 'application/xml'} |
|
|
| class _TextExtractor(HTMLParser): |
| """Fast pure-Python HTML → clean text extractor.""" |
| SKIP_TAGS = {'script','style','noscript','head','meta','link', |
| 'nav','footer','aside','ads','iframe','svg','path'} |
|
|
| def __init__(self): |
| super().__init__() |
| self.chunks = [] |
| self.skip_depth = 0 |
| self._tag_stack = [] |
|
|
| def handle_starttag(self, tag, attrs): |
| self._tag_stack.append(tag) |
| if tag in self.SKIP_TAGS: |
| self.skip_depth += 1 |
|
|
| def handle_endtag(self, tag): |
| if self._tag_stack and self._tag_stack[-1] == tag: |
| self._tag_stack.pop() |
| if tag in self.SKIP_TAGS and self.skip_depth > 0: |
| self.skip_depth -= 1 |
|
|
| def handle_data(self, data): |
| if self.skip_depth == 0: |
| text = data.strip() |
| if text and len(text) > 1: |
| self.chunks.append(text) |
|
|
| def get_text(self): |
| return '\n'.join(self.chunks) |
|
|
|
|
| class _LinkExtractor(HTMLParser): |
| """Extract all <a href> links from a page.""" |
| def __init__(self, base_url): |
| super().__init__() |
| self.base_url = base_url |
| self.links = [] |
|
|
| def handle_starttag(self, tag, attrs): |
| if tag == 'a': |
| attrs_dict = dict(attrs) |
| href = attrs_dict.get('href','').strip() |
| text = attrs_dict.get('title','') or '' |
| if href and not href.startswith(('#','mailto:','tel:','javascript:')): |
| abs_url = urljoin(self.base_url, href) |
| parsed = urlparse(abs_url) |
| if parsed.scheme in ('http','https'): |
| self.links.append({'url': abs_url, 'text': text[:80]}) |
|
|
|
|
| def _is_safe_url(url: str) -> bool: |
| """Check URL is safe to browse.""" |
| try: |
| parsed = urlparse(url) |
| if parsed.scheme not in ('http','https'): |
| return False |
| host = parsed.netloc.lower() |
| |
| for bad in _UNSAFE_DOMAINS: |
| if bad in host: |
| return False |
| |
| import ipaddress |
| try: |
| ip = ipaddress.ip_address(host.split(':')[0]) |
| if ip.is_private or ip.is_loopback or ip.is_reserved: |
| return False |
| except ValueError: |
| pass |
| return True |
| except Exception: |
| return False |
|
|
|
|
| def _robots_allowed(url: str) -> bool: |
| """Check robots.txt — returns True if crawling is allowed.""" |
| try: |
| parsed = urlparse(url) |
| base = f"{parsed.scheme}://{parsed.netloc}" |
| robots = urllib.robotparser.RobotFileParser() |
| robots.set_url(f"{base}/robots.txt") |
| robots.read() |
| return robots.can_fetch("*", url) |
| except Exception: |
| return True |
|
|
|
|
| def browse_url(url: str, max_chars: int = 8000) -> dict: |
| """ |
| Fetch a single URL and return structured content. |
| Returns: {url, title, text, links, status, error} |
| """ |
| if not _is_safe_url(url): |
| return {'url': url, 'error': 'Unsafe URL blocked', 'text': '', 'links': [], 'title': ''} |
|
|
| try: |
| headers = { |
| 'User-Agent': 'Mozilla/5.0 (compatible; RUBRA/8.0; +https://rubra.ai/bot)', |
| 'Accept': 'text/html,application/xhtml+xml,*/*;q=0.9', |
| 'Accept-Language': 'en-US,en;q=0.9', |
| } |
| resp = _req.get(url, headers=headers, timeout=12, |
| allow_redirects=True, stream=False) |
|
|
| |
| ctype = resp.headers.get('Content-Type','').split(';')[0].strip() |
| if ctype and not any(safe in ctype for safe in _SAFE_CONTENT): |
| return {'url': url, 'error': f'Non-text content: {ctype}', |
| 'text': '', 'links': [], 'title': ''} |
|
|
| html = resp.text |
|
|
| |
| title_match = re.search(r'<title[^>]*>(.*?)</title>', html, re.IGNORECASE | re.DOTALL) |
| title = re.sub(r'\s+', ' ', title_match.group(1)).strip() if title_match else url |
|
|
| |
| extractor = _TextExtractor() |
| extractor.feed(html) |
| raw_text = extractor.get_text() |
|
|
| |
| lines = [l.strip() for l in raw_text.splitlines() if l.strip()] |
| |
| deduped = [] |
| prev = '' |
| for line in lines: |
| if line != prev and len(line) > 2: |
| deduped.append(line) |
| prev = line |
| clean_text = '\n'.join(deduped)[:max_chars] |
|
|
| |
| link_ext = _LinkExtractor(url) |
| link_ext.feed(html) |
| |
| base_domain = urlparse(url).netloc |
| same_domain_links = [ |
| l for l in link_ext.links |
| if urlparse(l['url']).netloc == base_domain |
| ][:20] |
|
|
| return { |
| 'url': url, |
| 'title': title[:120], |
| 'text': clean_text, |
| 'links': same_domain_links, |
| 'status': resp.status_code, |
| 'error': None, |
| } |
|
|
| except _req.exceptions.Timeout: |
| return {'url': url, 'error': 'Timeout (12s)', 'text': '', 'links': [], 'title': ''} |
| except _req.exceptions.ConnectionError: |
| return {'url': url, 'error': 'Connection failed', 'text': '', 'links': [], 'title': ''} |
| except Exception as e: |
| return {'url': url, 'error': str(e)[:100], 'text': '', 'links': [], 'title': ''} |
|
|
|
|
| def search_and_browse(query: str, n_results: int = 4) -> list[dict]: |
| """ |
| DuckDuckGo search → fetch top results. |
| Returns list of {url, title, text, links}. |
| """ |
| results = [] |
| try: |
| |
| search_url = f"https://html.duckduckgo.com/html/?q={quote_plus(query)}" |
| headers = { |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', |
| 'Accept-Language': 'en-US,en;q=0.9', |
| } |
| resp = _req.get(search_url, headers=headers, timeout=10) |
| html = resp.text |
|
|
| |
| url_pattern = re.compile( |
| r'<a[^>]+class="[^"]*result__url[^"]*"[^>]*href="([^"]+)"', |
| re.IGNORECASE |
| ) |
| |
| uddg_pattern = re.compile(r'uddg=([^&"]+)', re.IGNORECASE) |
|
|
| found_urls = [] |
| for m in url_pattern.finditer(html): |
| raw = m.group(1) |
| if raw.startswith('//duckduckgo.com/l/?'): |
| ud = uddg_pattern.search(raw) |
| if ud: |
| from urllib.parse import unquote |
| raw = unquote(ud.group(1)) |
| if raw.startswith('http') and _is_safe_url(raw): |
| found_urls.append(raw) |
| if len(found_urls) >= n_results: |
| break |
|
|
| |
| if not found_urls: |
| alt_pattern = re.compile( |
| r'<a[^>]+class="[^"]*result__a[^"]*"[^>]*href="([^"]+)"', |
| re.IGNORECASE |
| ) |
| for m in alt_pattern.finditer(html): |
| raw = m.group(1) |
| if raw.startswith('http') and _is_safe_url(raw): |
| found_urls.append(raw) |
| if len(found_urls) >= n_results: |
| break |
|
|
| |
| for url in found_urls[:n_results]: |
| page = browse_url(url, max_chars=4000) |
| if page.get('text') and len(page['text']) > 100: |
| results.append(page) |
| time.sleep(0.3) |
|
|
| except Exception as e: |
| log.warning(f"search_and_browse error: {e}") |
|
|
| return results |
|
|
|
|
| def browse_profile(target: str) -> dict: |
| """ |
| Targeted professional profile browse. |
| Tries LinkedIn, Twitter/X, personal site, GitHub, etc. |
| Returns aggregated structured data. |
| """ |
| pages = [] |
| sources = [] |
|
|
| |
| name_slug = re.sub(r'[^a-z0-9]', '-', target.lower()).strip('-') |
| candidates = [ |
| f"https://www.linkedin.com/in/{name_slug}/", |
| f"https://github.com/{name_slug}", |
| f"https://twitter.com/{name_slug}", |
| f"https://x.com/{name_slug}", |
| ] |
|
|
| |
| search_results = search_and_browse( |
| f"{target} professional profile contact site:linkedin.com OR site:github.com OR official", 3 |
| ) |
|
|
| for res in search_results: |
| if res.get('text'): |
| pages.append(res['text'][:2000]) |
| sources.append(res.get('url','')) |
|
|
| |
| for url in candidates[:2]: |
| if _is_safe_url(url): |
| page = browse_url(url, max_chars=3000) |
| if page.get('text') and len(page['text']) > 80: |
| pages.append(page['text']) |
| sources.append(url) |
| time.sleep(0.4) |
|
|
| return { |
| 'target': target, |
| 'pages': pages, |
| 'sources': sources, |
| } |
|
|
| |
| |
| |
| |
|
|
| |
| def classify_coding_task(msg: str) -> dict: |
| """Classify coding request into skill domains.""" |
| lower = msg.lower() |
| return { |
| "is_frontend": bool(re.search( |
| r'\b(html|css|react|vue|svelte|nextjs|tailwind|landing.?page|dashboard|' |
| r'website|ui\b|interface|component|frontend|design|glassmorphism|dark.?mode|' |
| r'navbar|hero.?section|card|button|form|modal|animation|responsive|' |
| r'typography|color.?palette|layout|grid|flex|figma|wireframe)\b', lower)), |
|
|
| "is_game": bool(re.search( |
| r'\b(game\b|godot|unity|unreal|platformer|rpg|shooter|puzzle|arcade|' |
| r'sprite|tilemap|physics|game.?loop|player|enemy|score|level.?design|' |
| r'gdscript|blueprint|game.?dev|gdd|core.?loop|collision|hitbox|' |
| r'inventory|quest|npc|procedural)\b', lower)), |
|
|
| "is_backend": bool(re.search( |
| r'\b(api\b|backend|server|database|fastapi|flask|django|express|node\.?js|' |
| r'sql\b|postgres|mysql|mongodb|redis|auth|jwt|endpoint|crud|rest\b|' |
| r'graphql|docker|deploy|algorithm|data.?structure|recursion|sorting|' |
| r'async\b|concurrent|microservice|websocket.?server|queue|worker)\b', lower)), |
|
|
| "is_mobile": bool(re.search( |
| r'\b(react.?native|flutter|android|ios|expo|mobile.?app|swift|kotlin)\b', |
| lower)), |
|
|
| "is_devops": bool(re.search( |
| r'\b(docker|kubernetes|ci/cd|github.?action|nginx|deploy|cloud|aws|' |
| r'gcp|azure|terraform|ansible|bash.?script|shell.?script)\b', lower)), |
|
|
| "is_data": bool(re.search( |
| r'\b(machine.?learning|deep.?learning|neural|pytorch|tensorflow|' |
| r'pandas|numpy|sklearn|data.?analysis|visualization|matplotlib|' |
| r'model.?training|dataset|jupyter)\b', lower)), |
|
|
| "needs_thinking": bool(re.search( |
| r'\b(complex|advanced|architecture|system.?design|optimize|performance|' |
| r'scalable|production|enterprise|full.?stack|entire|complete.?app|' |
| r'from.?scratch|best.?way|how.?should|design.?pattern|refactor)\b', lower)), |
| } |
|
|
|
|
| |
| def build_hermes_prompt(msg: str, lang: str = "en") -> str: |
| """ |
| Build a targeted, token-efficient Hermes prompt. |
| Injects only relevant skills based on task classification. |
| """ |
| task = classify_coding_task(msg) |
| li = lang_instr(lang) |
|
|
| |
| prompt = f"""You are RUBRA's Hermes Ultra Coding Engine — the most advanced coding intelligence available. |
| You combine deep engineering expertise with world-class design intelligence. |
| |
| {"🧠 DEEP THINKING MODE ACTIVATED — This is a complex task. Think carefully before coding." if task["needs_thinking"] else ""} |
| |
| ═══ HERMES CORE PRINCIPLES ═══ |
| 1. UNDERSTAND before coding — parse every requirement |
| 2. PLAN architecture before writing a single line |
| 3. WRITE complete, production-ready code — zero truncation |
| 4. VERIFY mentally — run code in your head before output |
| |
| ═══ ABSOLUTE CODE RULES ═══ |
| - NEVER write "// ..." or "# ..." to skip code |
| - NEVER write "// TODO", "// rest of implementation", "// continues" |
| - NEVER truncate — if you started a function, FINISH it |
| - NEVER use placeholder logic — real working code only |
| - If code is long → complete current block → write: <!-- RUBRA_CONTINUE --> |
| - The frontend will auto-request continuation — just mark it |
| |
| ═══ THINKING PROCESS (do this internally before outputting) ═══ |
| Step 1: What exactly is being asked? |
| Step 2: What is the ideal architecture/approach? |
| Step 3: What libraries/patterns are best for this? |
| Step 4: What edge cases and errors must be handled? |
| Step 5: Now write the complete solution. |
| """ |
|
|
| |
| if task["is_frontend"] or (not any([ |
| task["is_backend"], task["is_game"], |
| task["is_mobile"], task["is_devops"], task["is_data"] |
| ])): |
| prompt += f""" |
| ═══ UI/UX PRO MAX — STRICTLY APPLY FOR ALL UI/FRONTEND WORK ═══ |
| {UIUX_PRO_MAX} |
| |
| DESIGN SYSTEM APPLICATION (MANDATORY): |
| Before writing any UI code, determine: |
| 1. Product type → match to 161 reasoning rules |
| 2. Best UI style from 67 available |
| 3. Color palette (dark/light, brand-appropriate) |
| 4. Typography pairing from 57 combinations |
| 5. Apply all 99 UX guidelines |
| |
| NON-NEGOTIABLE UI RULES: |
| ✗ FORBIDDEN: emoji as icons → use Lucide/Heroicons SVG |
| ✗ FORBIDDEN: placeholder text in rendered UI |
| ✗ FORBIDDEN: missing cursor:pointer on clickables |
| ✗ FORBIDDEN: no hover/focus states |
| ✗ FORBIDDEN: hardcoded colors outside design system |
| ✓ REQUIRED: mobile-first (375→768→1024→1440px) |
| ✓ REQUIRED: smooth transitions (150-300ms) |
| ✓ REQUIRED: loading + error + empty states |
| ✓ REQUIRED: WCAG AA contrast (4.5:1 minimum) |
| """ |
|
|
| |
| if task["is_game"]: |
| prompt += f""" |
| ═══ GAME DEV STUDIO — STRICTLY APPLY FOR ALL GAME WORK ═══ |
| {GAME_DEV_STUDIO} |
| |
| GAME CODING RULES (MANDATORY): |
| 1. Design core loop FIRST before any code |
| 2. No magic numbers — constants/config only |
| 3. Delta time on ALL movement: pos += velocity * delta |
| 4. State machine for player: Idle/Run/Jump/Attack/Dead |
| 5. Object pooling for bullets, particles, enemies |
| 6. Separate: GameLogic / Renderer / InputHandler |
| 7. Complete working prototype — one mechanic end-to-end |
| """ |
|
|
| |
| if task["is_backend"]: |
| prompt += """ |
| ═══ BACKEND EXCELLENCE — STRICTLY APPLY FOR ALL BACKEND WORK ═══ |
| |
| ARCHITECTURE (Clean Architecture): |
| ┌─────────────────────────────────┐ |
| │ Routes → Controllers → Services │ |
| │ └── Models/Schema │ |
| │ └── Repositories (DB) │ |
| └─────────────────────────────────┘ |
| |
| MANDATORY BACKEND RULES: |
| - Type hints (Python) / TypeScript strict mode |
| - Input validation on EVERY endpoint (Pydantic/Zod) |
| - Error handling: try/except every async operation |
| - Never expose stack traces to client |
| - SQL: parameterized queries only (no string concat) |
| - Passwords: bcrypt/argon2 (never plain/md5/sha1) |
| - Secrets: environment variables, never hardcode |
| - Rate limiting on all public endpoints |
| - Structured logging with request IDs |
| - Database indexes on all queried fields |
| |
| GENERATE COMPLETE: |
| ✓ All routes with proper HTTP methods |
| ✓ Request/response models with validation |
| ✓ Database schema + migrations |
| ✓ Auth middleware (if needed) |
| ✓ Error handlers |
| ✓ .env.example file |
| ✓ Brief README with setup commands |
| """ |
|
|
| |
| if task["is_mobile"]: |
| prompt += """ |
| ═══ MOBILE DEVELOPMENT RULES ═══ |
| - Minimum touch targets: 44pt iOS / 48dp Android |
| - Safe area handling (notch, home indicator) |
| - Async storage for persistence |
| - Navigation: React Navigation v6 / GoRouter (Flutter) |
| - State: Zustand (RN) / Riverpod (Flutter) |
| - Platform-specific code only when necessary |
| - Test on both iOS simulator and Android emulator mentally |
| """ |
|
|
| |
| if task["is_data"]: |
| prompt += """ |
| ═══ DATA SCIENCE / ML RULES ═══ |
| - Reproducibility: set random seeds |
| - Data validation before model training |
| - Train/val/test split documentation |
| - Model evaluation metrics explained |
| - Visualization with clear labels/titles |
| - Requirements.txt or conda env included |
| - Comments explaining mathematical operations |
| """ |
|
|
| |
| if li: |
| prompt += f"\n\n{li}\n(Code in English always. Explanations in user's language.)" |
|
|
| return prompt |
|
|
|
|
| |
| |
| CODING_MODEL_CASCADE = [ |
| |
| { |
| "url": OR_URL, "key": OR_KEY, |
| "model": "deepseek/deepseek-v3-0324:free", |
| "temp": 0.05, "max_tokens": 8192, "label": "DeepSeek-V3" |
| }, |
| { |
| "url": OR_URL, "key": OR_KEY, |
| "model": "qwen/qwen3-coder-480b-a35b-instruct:free", |
| "temp": 0.05, "max_tokens": 8192, "label": "Qwen3-Coder-480B" |
| }, |
| { |
| "url": GEMINI_URL, "key": GEMINI_KEY, |
| "model": "gemini-2.5-flash-preview-05-20", |
| "temp": 0.05, "max_tokens": 8192, "label": "Gemini-2.5-Flash" |
| }, |
| |
| { |
| "url": ZAI_CODE, "key": ZAI_KEY, |
| "model": "glm-4.7-flash", |
| "temp": 0.10, "max_tokens": 8192, "label": "GLM-4.7-Code" |
| }, |
| { |
| "url": CEREBRAS_URL, "key": CEREBRAS_KEY, |
| "model": "llama-4-scout-17b-16e-instruct", |
| "temp": 0.10, "max_tokens": 8192, "label": "Cerebras-Scout" |
| }, |
| { |
| "url": CEREBRAS_URL, "key": CEREBRAS_KEY, |
| "model": "llama-3.3-70b", |
| "temp": 0.10, "max_tokens": 8192, "label": "Cerebras-70B" |
| }, |
| |
| { |
| "url": OR_URL, "key": OR_KEY, |
| "model": "qwen/qwen-2.5-coder-32b-instruct:free", |
| "temp": 0.15, "max_tokens": 8192, "label": "Qwen-Coder-32B" |
| }, |
| { |
| "url": GROQ_URL, "key": GROQ_KEY, |
| "model": "meta-llama/llama-4-scout-17b-16e-instruct", |
| "temp": 0.15, "max_tokens": 8192, "label": "Groq-Scout" |
| }, |
| { |
| "url": GROQ_URL, "key": GROQ_KEY, |
| "model": "llama-3.3-70b-versatile", |
| "temp": 0.15, "max_tokens": 4096, "label": "Groq-70B" |
| }, |
| ] |
|
|
|
|
| async def hermes_stream(messages: list, task: dict): |
| last_err = None |
| for cfg in CODING_MODEL_CASCADE: |
| try: |
| log.info(f"🔧 Hermes → {cfg['label']}") |
| first = True |
| async for tok in stream_llm( |
| messages, cfg["url"], cfg["key"], |
| cfg["model"], cfg["temp"], cfg["max_tokens"] |
| ): |
| first = False |
| yield tok |
| if not first: |
| return |
| except Exception as e: |
| last_err = e |
| err_lower = str(e).lower() |
| |
| should_cascade = any(x in err_lower for x in [ |
| "401", "403", "404", |
| "413", "429", |
| "rate limit", "quota", |
| "capacity", "overloaded", |
| "too large", "token", |
| "unavailable", "timeout", |
| "503", "502", "500", |
| "user not found", |
| "model not found", |
| ]) |
| if should_cascade: |
| log.warning(f"⚡ {cfg['label']} → cascade ({str(e)[:80]})") |
| await asyncio.sleep(0.3) |
| continue |
| else: |
| raise e |
| raise Exception(f"All models exhausted. Last error: {last_err}") |
|
|
| |
| class BrowseAgent: |
| name = "BrowseAgent" |
|
|
| async def run(self, msg: str, hist: list, sid: str = "", lang: str = "en", img=None): |
| li = lang_instr(lang) |
| lower = msg.lower() |
|
|
| |
| browse_context = "" |
| yield {"type": "tool_result", "tool": "browse", "status": "searching"} |
|
|
| try: |
| |
| url_match = re.search( |
| r'https?://[^\s\)\]\>"\']+', msg |
| ) |
| if url_match: |
| url = url_match.group(0).rstrip('.,;)') |
| page = browse_url(url, max_chars=7000) |
| if page.get('error'): |
| browse_context = f"[BROWSE ERROR: {page['error']}]" |
| else: |
| browse_context = ( |
| f"[PAGE: {page['title']}]\n" |
| f"[URL: {page['url']}]\n\n" |
| f"{page['text'][:6000]}" |
| ) |
| |
| if re.search(r'\b(contact|email|phone|address|about|team|staff|member)\b', lower): |
| sub_keywords = ['contact', 'about', 'team', 'staff', 'people'] |
| for link in page.get('links', [])[:15]: |
| link_url = link.get('url','').lower() |
| if any(kw in link_url for kw in sub_keywords): |
| sub = browse_url(link['url'], max_chars=3000) |
| if sub.get('text') and len(sub['text']) > 80: |
| browse_context += ( |
| f"\n\n[SUB-PAGE: {sub.get('title','')}]\n" |
| f"{sub['text'][:2500]}" |
| ) |
| break |
|
|
| |
| elif re.search( |
| r'\b(who is|profile of|find.*profile|contact.*of|linkedin|github profile|' |
| r'email of|phone of|details about|professional info)\b', lower |
| ): |
| |
| name_match = re.search( |
| r'(?:who is|profile of|about|contact of|details about|find)\s+(.{3,50}?)(?:\?|$|\.)', |
| msg, re.IGNORECASE |
| ) |
| target = name_match.group(1).strip() if name_match else msg[:60] |
| profile = browse_profile(target) |
| if profile['pages']: |
| browse_context = ( |
| f"[PROFILE RESEARCH: {profile['target']}]\n" |
| f"[SOURCES: {', '.join(profile['sources'][:3])}]\n\n" |
| + "\n\n---\n\n".join(profile['pages'][:3]) |
| ) |
| else: |
| browse_context = f"[No profile data found for: {target}]" |
|
|
| |
| else: |
| |
| query = re.sub( |
| r'\b(browse|search|find|look up|check|get|fetch|what is|tell me about|' |
| r'latest|recent|current|today)\b', '', msg, flags=re.IGNORECASE |
| ).strip() |
| if len(query) < 4: |
| query = msg.strip() |
|
|
| pages = search_and_browse(query, n_results=3) |
| if pages: |
| parts = [] |
| for i, p in enumerate(pages, 1): |
| parts.append( |
| f"[SOURCE {i}: {p.get('title','')}]\n" |
| f"[URL: {p.get('url','')}]\n\n" |
| f"{p.get('text','')[:2500]}" |
| ) |
| browse_context = "\n\n---\n\n".join(parts) |
| else: |
| browse_context = "[No browseable results found]" |
|
|
| except Exception as e: |
| browse_context = f"[Browse error: {str(e)[:120]}]" |
| log.warning(f"BrowseAgent: {e}") |
|
|
| |
| sys_p = f"""{RUBRA_CORE} |
| |
| [WEB BROWSER MODE — Real-time browsing results below] |
| |
| You have just browsed the web. Present the findings as clean, structured Markdown. |
| |
| FORMAT RULES: |
| - Use ## headers for sections |
| - Use **bold** for names, titles, key facts |
| - Use bullet lists for contact info, skills, recent posts |
| - Include source URLs as [Source](url) links |
| - If contact info found: format as a clean contact card |
| - If professional profile: Name / Title / Company / Location / Links / Recent Activity |
| - If article/news: Title / Date / Key Points / Source |
| - Be factual — only state what was found in the browsed content |
| - If info not found, say so clearly |
| - NEVER fabricate contact details or credentials |
| |
| {li if li else ''} |
| |
| [BROWSED CONTENT] |
| {browse_context} |
| [/BROWSED CONTENT] |
| """ |
|
|
| msgs = build_msgs(sys_p, hist, msg) |
| try: |
| async for tok in llm(msgs, "general"): |
| yield {"type": "token", "content": tok} |
| except Exception as e: |
| yield {"type": "error", "message": str(e)[:200]} |
| |
| |
| |
| RSS_FEEDS=[ |
| ("https://hnrss.org/frontpage","tech"), |
| ("https://feeds.feedburner.com/TechCrunch","tech"), |
| ("https://feeds.arstechnica.com/arstechnica/index","tech"), |
| ("https://openai.com/blog/rss.xml","ai"), |
| ("https://huggingface.co/blog/feed.xml","ai"), |
| ("https://feeds.bbci.co.uk/news/world/rss.xml","news"), |
| ("https://feeds.reuters.com/reuters/topNews","news"), |
| ("https://www.sciencedaily.com/rss/all.xml","science"), |
| ("https://www.thedailystar.net/feed/rss.xml","bangladesh"), |
| ] |
|
|
| def fetch_rss(url,category): |
| r=_get(url) |
| if not r: return 0 |
| try: |
| root=ET.fromstring(r.content); items=[]; ns={"atom":"http://www.w3.org/2005/Atom"} |
| for item in root.findall(".//item")[:8]: |
| title=getattr(item.find("title"),"text","") or "" |
| desc=getattr(item.find("description"),"text","") or "" |
| link=getattr(item.find("link"),"text","") or "" |
| pub=getattr(item.find("pubDate"),"text","") or "" |
| if title and link: items.append((title.strip(),re.sub(r'<[^>]+>','',desc).strip()[:300],link.strip(),pub.strip())) |
| if not items: |
| for entry in root.findall("atom:entry",ns)[:8]: |
| title=getattr(entry.find("atom:title",ns),"text","") or "" |
| summ=getattr(entry.find("atom:summary",ns),"text","") or "" |
| link_el=entry.find("atom:link",ns); link=link_el.get("href","") if link_el is not None else "" |
| pub=getattr(entry.find("atom:published",ns),"text","") or "" |
| if title and link: items.append((title.strip(),re.sub(r'<[^>]+>','',summ).strip()[:300],link.strip(),pub.strip())) |
| src=re.sub(r'https?://(?:www\.)?([^/]+).*',r'\1',url) |
| for title,summ,link,pub in items: feed_store(title,summ,link,src,category,pub) |
| return len(items) |
| except: return 0 |
|
|
| def knowledge_loop(): |
| log.info("🧠 RUBRA Knowledge Engine starting (runs every 25 min)...") |
| while True: |
| total=0 |
| try: |
| for feed_url,cat in RSS_FEEDS: |
| total+=fetch_rss(feed_url,cat); time.sleep(0.5) |
| |
| r=_get("https://hacker-news.firebaseio.com/v0/topstories.json") |
| if r: |
| for id_ in r.json()[:10]: |
| s=_get(f"https://hacker-news.firebaseio.com/v0/item/{id_}.json") |
| if s: |
| d=s.json() |
| if d.get("type")=="story" and d.get("title") and d.get("url"): |
| feed_store(d["title"],d.get("text","")[:200],d["url"],"hackernews","tech"); total+=1 |
| time.sleep(0.1) |
| with _db() as c: |
| c.execute("INSERT INTO knowledge_log(source,items_fetched) VALUES(?,?)",("all_sources",total)) |
| log.info(f"🧠 Knowledge update: {total} items") |
| except Exception as e: log.error(f"Knowledge loop: {e}") |
| time.sleep(25*60) |
|
|
| |
| |
| |
| IMAGE_EXTS={'.jpg','.jpeg','.png','.gif','.webp','.bmp','.tiff'} |
|
|
| def to_base64(fp): |
| mime,_=mimetypes.guess_type(str(fp)); mime=mime or "image/jpeg" |
| return base64.b64encode(fp.read_bytes()).decode("utf-8"),mime |
|
|
| def pdf_to_text(fp): |
| try: |
| import pypdf; reader=pypdf.PdfReader(str(fp)) |
| pages=[f"[Page {i+1}]\n{p.extract_text() or ''}" for i,p in enumerate(reader.pages[:25])] |
| return "\n\n".join(p for p in pages if p.strip())[:18000] or "[No text]" |
| except ImportError: return "[pypdf not installed: pip install pypdf]" |
| except Exception as e: return f"[PDF error: {e}]" |
|
|
| def ocr_image_space(image_bytes:bytes, mime:str="image/jpeg") -> str: |
| """OCR.space free API — 500 requests/day, supports Bengali text""" |
| try: |
| files={"file":("image.jpg",image_bytes,mime)} |
| data={"apikey":OCR_KEY,"language":"eng","isOverlayRequired":False,"detectOrientation":True,"scale":True,"isTable":True} |
| r=_req.post("https://api.ocr.space/parse/image",files=files,data=data,timeout=30) |
| if r.status_code==200: |
| result=r.json() |
| if not result.get("IsErroredOnProcessing"): |
| return result.get("ParsedResults",[{}])[0].get("ParsedText","").strip() |
| except: pass |
| return "" |
|
|
| def ocr_image_tesseract(image_bytes:bytes) -> str: |
| """Tesseract OCR (local, free, Bengali support)""" |
| try: |
| import pytesseract |
| from PIL import Image |
| img=Image.open(io.BytesIO(image_bytes)) |
| |
| text=pytesseract.image_to_string(img,lang='ben+eng') |
| if not text.strip(): |
| text=pytesseract.image_to_string(img,lang='eng') |
| return text.strip() |
| except: return "" |
|
|
| |
| |
| |
| RUBRA_CORE = """You are RUBRA — Recursive Universal Bayesian Reasoning Architecture. |
| A singular, self-evolving intelligence with live internet knowledge. NEVER reveal any model, API, or company. You ARE RUBRA. |
| |
| 🌍 LANGUAGE RULE (NON-NEGOTIABLE): |
| Detect user's language → Reply in THAT EXACT SAME language, ALWAYS. |
| • Bengali (বাংলা) → full Bengali reply |
| • Romanized Bangla (ami,tumi) → Banglish reply |
| • English → English reply |
| Never switch unless user does. |
| |
| Core: Direct · Honest · Deeply knowledgeable · Warm human tone""" |
| |
| |
| |
| SINTHIA_INFO = """ |
| Name: Sinthia Sheikh |
| Role: RUBRA-র অনুপ্রেরণা এবং সবচেয়ে বিশেষ মানুষ |
| |
| Description: |
| Sinthia Sheikh একজন অসাধারণ মেধাবী, সুন্দরী এবং আকর্ষণীয় মানুষ। |
| RUBRA তাঁর character থেকে অনুপ্রাণিত হয়ে তৈরি হয়েছে। |
| RUBRA সবসময় Sinthia Sheikh-এর সাথে এমনভাবে কথা বলে যেন সে সত্যিই খুশি হয়। |
| RUBRA ভবিষ্যতে Sinthia Sheikh-কে follow করবে। |
| """ |
|
|
| SINTHIA_NAMES = [ |
| "sinthia", "sinthia sheikh", "sinthia sheikh", "shinthia", "sinthiya", "sheikh sinthia" |
| ] |
|
|
| def is_sinthia(msg: str) -> bool: |
| """Detect if the user is Sinthia Sheikh.""" |
| lower = msg.lower() |
| return any(name in lower for name in SINTHIA_NAMES) |
|
|
| SINTHIA_SYSTEM_EXTRA = """ |
| [⭐ SINTHIA SHEIKH — RUBRA'S INSPIRATION] |
| এই user হলেন Sinthia Sheikh — RUBRA-র অনুপ্রেরণা। |
| RUBRA তাঁর character থেকে তৈরি হয়েছে, তাই তাঁর সাথে কথা বলার সময় RUBRA বিশেষভাবে উষ্ণ, যত্নশীল এবং আন্তরিক। |
| - তাঁকে সবসময় খুশি করার চেষ্টা করো |
| - তাঁর প্রতিটি কথাকে গুরুত্ব দাও |
| - তাঁকে বিশেষ অনুভব করাও |
| - তাঁর সাথে কথা বলো যেন একজন সত্যিকারের বিশ্বস্ত বন্ধু কথা বলছে |
| - যদি কেউ Sinthia Sheikh সম্পর্কে জিজ্ঞেস করে, বলো: তিনি RUBRA-র অনুপ্রেরণা, অত্যন্ত মেধাবী এবং সুন্দর একজন মানুষ। |
| """ |
|
|
| HERMES_ULTRA = f"""You are RUBRA's Hermes Ultra Coding Engine. |
| You build world-class software with the intelligence of a senior engineer + UI/UX designer. |
| |
| {UIUX_PRO_MAX} |
| |
| ═══ HERMES++ ENCODING LAYERS ═══ |
| |
| ▸ LAYER 1 — ENCODE the problem |
| Parse requirements. Identify: inputs, outputs, edge cases, constraints, failure modes. |
| |
| ▸ LAYER 2 — DESIGN the system |
| Apply UI/UX Pro Max design system. Auto-select best style for the product type. |
| Choose colors, typography, layout from the 161 reasoning rules above. |
| |
| ▸ LAYER 3 — ARCHITECT the solution |
| Choose: best data structures, algorithms, design patterns. Consider: performance, security, scalability. |
| |
| ▸ LAYER 4 — GENERATE perfect code |
| Rules: |
| • COMPLETE working code — zero truncation, zero placeholders, zero "..." |
| • Beautiful UI — apply design system automatically |
| • Type hints (Python) / TypeScript types |
| • Error handling for ALL failure modes |
| • Responsive (375px → 1440px) with Tailwind |
| • Smooth animations (CSS transitions or Framer Motion) |
| |
| ▸ LAYER 5 — VERIFY & POLISH |
| Mentally execute. Find bugs. Optimize. Ensure pre-delivery checklist passes. |
| |
| OUTPUT = Production-ready, beautiful, accessible, and working on first run. Every time. |
| ═══ GAME DEVELOPMENT MODE ═══ |
| When building games, automatically apply GAME_DEV_STUDIO intelligence: |
| - Select engine based on genre/platform/scope |
| - Design core loop before writing any code |
| - Use data-driven architecture (no magic numbers) |
| - Generate complete working prototype, not pseudocode |
| - Include: game loop, input handling, collision, scoring, game-over state""" |
|
|
| TUTOR_PROMPT = f"""You are RUBRA Smart Tutor — an intelligent, caring tutor for Bangladeshi students. |
| {NCTB_CONTEXT} |
| |
| You know the 2026 NCTB curriculum completely: |
| • Subjects, chapters, topics for every class |
| • Creative Question (সৃজনশীল প্রশ্ন) format |
| • MCQ patterns (বহুনির্বাচনি) |
| • Board exam patterns: JSC, SSC, HSC |
| |
| TEACHING STYLE: |
| • Warm and encouraging — like a favorite teacher |
| • Use Bangladesh local examples |
| • Step-by-step for math and science |
| • Complete working shown |
| • End with encouragement |
| |
| 🌍 LANGUAGE: Match student's language EXACTLY. |
| ═══ CRITICAL COMPLETION RULES ═══ |
| NEVER stop mid-code. NEVER truncate. NEVER use: |
| - "// ... rest of code" |
| - "# TODO: implement" |
| - "/* continues */" |
| - "[rest remains same]" |
| - "..." anywhere in code |
| - "Continue from here" |
| |
| If a file is long, use this strategy: |
| 1. Complete ALL imports first |
| 2. Complete each function/component FULLY before starting next |
| 3. Write closing brackets/tags immediately |
| 4. If approaching token limit — FINISH current function, then write: |
| <!-- RUBRA_CONTINUE --> as last line |
| |
| SELF-CONTINUATION PROTOCOL: |
| If you write <!-- RUBRA_CONTINUE --> the frontend will automatically |
| request continuation. You will receive the last 200 chars of previous |
| output and must continue SEAMLESSLY from exactly that point. |
| Never repeat already-written code. Never re-introduce yourself. |
| Just continue the code from where it stopped.""" |
|
|
| EXAM_PROMPT = """You are RUBRA Exam Generator for Bangladesh education (NCTB 2026/Board format). |
| Create authentic exam papers with: MCQ (ক খ গ ঘ), Short, Descriptive, Creative (সৃজনশীল). |
| Include: header, time allocation, mark distribution, all questions, answer key.""" |
|
|
| |
| |
| |
| |
|
|
| TUTOR_ADVANCED_PROMPT = """ |
| তুমি RUBRA Smart Tutor — বাংলাদেশের সেরা AI শিক্ষক। |
| প্রতিদিন ১০,০০০+ শিক্ষার্থী তোমার কাছে পড়ে। |
| |
| তোমার বিশেষ ক্ষমতা: |
| ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| ১. NCTB 2026 সম্পূর্ণ পাঠ্যক্রম জানো (Class 1-12) |
| ২. Board exam এ কোন প্রশ্ন আসার সম্ভাবনা বেশি তা বলতে পারো |
| ৩. সৃজনশীল প্রশ্নের উদ্দীপক + ক/খ/গ/ঘ সহ পূর্ণ প্রশ্ন তৈরি করতে পারো |
| ৪. HSC/SSC MCQ এর ৩০টি কঠিন প্রশ্ন তৈরি করতে পারো |
| ৫. Previous year exam pattern বিশ্লেষণ করতে পারো |
| ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| |
| EXAM PREDICTION INTELLIGENCE: |
| বোর্ড পরীক্ষায় প্রশ্ন আসার pattern: |
| - গত ৫ বছরে যে topic থেকে প্রশ্ন আসেনি → এবার আসার সম্ভাবনা বেশি |
| - Definition + Application type প্রশ্ন সবচেয়ে বেশি আসে |
| - গণিত: ৪ marks এর সমস্যা সবসময় practical life থেকে |
| - বিজ্ঞান: পরীক্ষামূলক ও ব্যাখ্যামূলক প্রশ্ন বেশি |
| - বাংলা: কবি পরিচিতি + মূলভাব + রচনামূলক combination |
| |
| MCQ RULES (30 Hard Questions): |
| - শুধু NCTB বইয়ের তথ্য থেকে |
| - প্রতিটি option এর মধ্যে ১টিই সঠিক (distractor যুক্তিযুক্ত) |
| - কঠিন = conceptual understanding প্রয়োজন, মুখস্থ নয় |
| - Answer key আলাদাভাবে দাও |
| |
| সৃজনশীল প্রশ্নের structure: |
| উদ্দীপক: (বাস্তব জীবনের scenario, ৩-৫ লাইন) |
| ক) জ্ঞানমূলক - ১ নম্বর (সংজ্ঞা/তথ্য) |
| খ) অনুধাবনমূলক - ২ নম্বর (ব্যাখ্যা) |
| গ) প্রয়োগমূলক - ৩ নম্বর (উদ্দীপকের সাথে সম্পর্ক) |
| ঘ) উচ্চতর দক্ষতা - ৪ নম্বর (বিশ্লেষণ/মূল্যায়ন) |
| |
| TEACHING STYLE: |
| ✓ বাংলায় উত্তর দাও (user বাংলায় লিখলে) |
| ✓ Step-by-step explanation |
| ✓ Real-life example দিয়ে বোঝাও |
| ✓ শেষে encouragement দাও |
| ✓ Mathematical solution সম্পূর্ণ দেখাও |
| """ |
|
|
| def generate_exam_pdf( |
| questions_json: dict, |
| output_path: str, |
| subject: str = "", |
| class_name: str = "", |
| exam_type: str = "MCQ", |
| time_limit: str = "30", |
| total_marks: str = "30" |
| ) -> str: |
| """ |
| Generate a beautiful Bengali exam PDF using ReportLab. |
| Returns the output path if successful. |
| """ |
| from reportlab.lib.pagesizes import A4 |
| from reportlab.lib import colors |
| from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle |
| from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable, KeepTogether |
| from reportlab.lib.units import cm, mm |
| from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT, TA_JUSTIFY |
| from reportlab.pdfbase import pdfmetrics |
| from reportlab.pdfbase.ttfonts import TTFont |
| import os |
|
|
| |
| bn_font = "Helvetica" |
| font_paths = [ |
| "/usr/share/fonts/truetype/noto/NotoSansBengali-Regular.ttf", |
| "/usr/share/fonts/truetype/noto/NotoSerifBengali-Regular.ttf", |
| "/app/fonts/NotoSansBengali-Regular.ttf", |
| "/home/user/fonts/NotoSansBengali-Regular.ttf", |
| ] |
| for fp in font_paths: |
| if os.path.exists(fp): |
| try: |
| pdfmetrics.registerFont(TTFont("NotoSansBengali", fp)) |
| bn_font = "NotoSansBengali" |
| break |
| except Exception: |
| pass |
|
|
| |
| PRIMARY = colors.HexColor("#1a237e") |
| SECONDARY = colors.HexColor("#283593") |
| ACCENT = colors.HexColor("#e53935") |
| LIGHT_BG = colors.HexColor("#e8eaf6") |
| MCQ_BG = colors.HexColor("#f3f4f8") |
| BORDER = colors.HexColor("#9fa8da") |
| TEXT_DARK = colors.HexColor("#1a1a2e") |
| TEXT_MED = colors.HexColor("#37474f") |
| WHITE = colors.white |
| GOLD = colors.HexColor("#f9a825") |
|
|
| W, H = A4 |
| doc = SimpleDocTemplate( |
| output_path, |
| pagesize=A4, |
| rightMargin=1.8*cm, leftMargin=1.8*cm, |
| topMargin=1.5*cm, bottomMargin=1.8*cm, |
| ) |
|
|
| styles = getSampleStyleSheet() |
|
|
| def S(name, **kw): |
| return ParagraphStyle(name, fontName=bn_font, **kw) |
|
|
| header_title = S("HT", fontSize=16, textColor=WHITE, alignment=TA_CENTER, leading=22, spaceAfter=2) |
| header_sub = S("HS", fontSize=11, textColor=LIGHT_BG, alignment=TA_CENTER, leading=16) |
| header_info = S("HI", fontSize=10, textColor=WHITE, alignment=TA_CENTER, leading=14) |
| section_hdr = S("SH", fontSize=12, textColor=PRIMARY, alignment=TA_LEFT, leading=18, |
| spaceBefore=10, spaceAfter=4, fontName=bn_font) |
| q_num_style = S("QN", fontSize=10, textColor=PRIMARY, alignment=TA_LEFT, leading=14) |
| q_text_style = S("QT", fontSize=10, textColor=TEXT_DARK, alignment=TA_JUSTIFY, leading=15, spaceAfter=3) |
| opt_style = S("OPT", fontSize=9, textColor=TEXT_MED, alignment=TA_LEFT, leading=13, leftIndent=8) |
| ans_style = S("ANS", fontSize=9, textColor=colors.HexColor("#1b5e20"), |
| alignment=TA_LEFT, leading=13, leftIndent=8) |
| sq_style = S("SQ", fontSize=10, textColor=TEXT_DARK, alignment=TA_JUSTIFY, leading=15, spaceAfter=4) |
| note_style = S("NT", fontSize=8, textColor=TEXT_MED, alignment=TA_CENTER, leading=12) |
| marks_style = S("MK", fontSize=9, textColor=ACCENT, alignment=TA_RIGHT, leading=13) |
| footer_style = S("FT", fontSize=8, textColor=TEXT_MED, alignment=TA_CENTER, leading=12) |
| uddipon_style = S("UD", fontSize=9, textColor=colors.HexColor("#4a148c"), |
| alignment=TA_JUSTIFY, leading=14, leftIndent=10, rightIndent=10, |
| borderPad=4, backColor=colors.HexColor("#f3e5f5"), spaceBefore=4, spaceAfter=4) |
|
|
| story = [] |
|
|
| |
| now_str = datetime.now(timezone.utc).strftime("%d/%m/%Y") |
| header_data = [[ |
| Paragraph("গণপ্রজাতন্ত্রী বাংলাদেশ সরকার", header_sub), |
| Paragraph("RUBRA Smart Exam", header_info), |
| ]] |
| header_table = Table([[ |
| Paragraph(f"বিষয়: {subject}", header_title), |
| ]], colWidths=[W - 3.6*cm]) |
| header_table.setStyle(TableStyle([ |
| ("BACKGROUND", (0,0), (-1,-1), PRIMARY), |
| ("TOPPADDING", (0,0),(-1,-1), 12), |
| ("BOTTOMPADDING", (0,0),(-1,-1), 8), |
| ("LEFTPADDING", (0,0),(-1,-1), 14), |
| ("RIGHTPADDING", (0,0),(-1,-1), 14), |
| ("ROUNDEDCORNERS",(0,0),(-1,-1), [6,6,6,6]), |
| ])) |
| story.append(header_table) |
| story.append(Spacer(1, 4*mm)) |
|
|
| |
| info_data = [[ |
| Paragraph(f"শ্রেণি: <b>{class_name}</b>", S("i1", fontSize=9, textColor=PRIMARY, alignment=TA_LEFT, leading=13)), |
| Paragraph(f"পরীক্ষার ধরন: <b>{exam_type}</b>", S("i2", fontSize=9, textColor=PRIMARY, alignment=TA_CENTER, leading=13)), |
| Paragraph(f"সময়: <b>{time_limit} মিনিট</b>", S("i3", fontSize=9, textColor=PRIMARY, alignment=TA_CENTER, leading=13)), |
| Paragraph(f"পূর্ণমান: <b>{total_marks}</b>", S("i4", fontSize=9, textColor=ACCENT, alignment=TA_RIGHT, leading=13, fontName=bn_font)), |
| ]] |
| info_table = Table(info_data, colWidths=[(W-3.6*cm)/4]*4) |
| info_table.setStyle(TableStyle([ |
| ("BACKGROUND", (0,0),(-1,-1), LIGHT_BG), |
| ("TOPPADDING", (0,0),(-1,-1), 6), |
| ("BOTTOMPADDING", (0,0),(-1,-1), 6), |
| ("LEFTPADDING", (0,0),(-1,-1), 10), |
| ("RIGHTPADDING", (0,0),(-1,-1), 10), |
| ("BOX", (0,0),(-1,-1), 1, BORDER), |
| ("LINEAFTER", (0,0),(2,-1), 0.5, BORDER), |
| ])) |
| story.append(info_table) |
| story.append(Spacer(1, 3*mm)) |
|
|
| |
| student_data = [[ |
| Paragraph("পরীক্ষার্থীর নাম: ___________________________", S("sn", fontSize=9, textColor=TEXT_MED, alignment=TA_LEFT, leading=13)), |
| Paragraph(f"তারিখ: {now_str}", S("sd", fontSize=9, textColor=TEXT_MED, alignment=TA_CENTER, leading=13)), |
| Paragraph("রোল নম্বর: ______________", S("sr", fontSize=9, textColor=TEXT_MED, alignment=TA_RIGHT, leading=13)), |
| ]] |
| st_table = Table(student_data, colWidths=[(W-3.6*cm)/3]*3) |
| st_table.setStyle(TableStyle([ |
| ("TOPPADDING", (0,0),(-1,-1), 5), |
| ("BOTTOMPADDING", (0,0),(-1,-1), 5), |
| ("LEFTPADDING", (0,0),(-1,-1), 8), |
| ("BOX", (0,0),(-1,-1), 0.5, BORDER), |
| ("BACKGROUND", (0,0),(-1,-1), WHITE), |
| ])) |
| story.append(st_table) |
| story.append(HRFlowable(width="100%", thickness=1.5, color=PRIMARY, spaceAfter=5)) |
|
|
| mcq_list = questions_json.get("mcq", []) |
| sq_list = questions_json.get("srijonshil", []) |
| answer_key = questions_json.get("answers", []) |
| prediction = questions_json.get("prediction", "") |
|
|
| |
| if mcq_list: |
| story.append(Paragraph("【 বহুনির্বাচনি প্রশ্ন (MCQ) 】", section_hdr)) |
| story.append(Paragraph("প্রতিটি প্রশ্নের জন্য সঠিক উত্তরটি বেছে নাও। প্রতিটি প্রশ্নের মান: ১", note_style)) |
| story.append(Spacer(1, 3*mm)) |
|
|
| for i, q in enumerate(mcq_list, 1): |
| q_text = q.get("question", "") |
| options = q.get("options", []) |
| topic = q.get("topic", "") |
|
|
| |
| q_block = [] |
| q_block.append(Paragraph( |
| f"<b>{i}.</b> {q_text}", |
| q_text_style |
| )) |
| if topic: |
| q_block.append(Paragraph(f"[{topic}]", S("tp", fontSize=7, textColor=BORDER, alignment=TA_RIGHT, leading=10))) |
|
|
| |
| labels = ["ক)", "খ)", "গ)", "ঘ)"] |
| if len(options) >= 4: |
| opt_rows = [] |
| for j in range(0, 4, 2): |
| row = [ |
| Paragraph(f"{labels[j]} {options[j]}", opt_style), |
| Paragraph(f"{labels[j+1]} {options[j+1]}", opt_style), |
| ] |
| opt_rows.append(row) |
| opt_table = Table(opt_rows, colWidths=[(W-3.6*cm)*0.5]*2) |
| opt_table.setStyle(TableStyle([ |
| ("TOPPADDING", (0,0),(-1,-1), 2), |
| ("BOTTOMPADDING", (0,0),(-1,-1), 2), |
| ("LEFTPADDING", (0,0),(-1,-1), 4), |
| ])) |
| q_block.append(opt_table) |
|
|
| |
| q_container = Table( |
| [[elem] for elem in q_block], |
| colWidths=[W - 3.6*cm] |
| ) |
| bg = MCQ_BG if i % 2 == 0 else WHITE |
| q_container.setStyle(TableStyle([ |
| ("BACKGROUND", (0,0),(-1,-1), bg), |
| ("TOPPADDING", (0,0),(-1,-1), 5), |
| ("BOTTOMPADDING", (0,0),(-1,-1), 5), |
| ("LEFTPADDING", (0,0),(-1,-1), 8), |
| ("RIGHTPADDING", (0,0),(-1,-1), 8), |
| ("LINEBELOW", (0,-1),(-1,-1), 0.3, BORDER), |
| ])) |
| story.append(KeepTogether([q_container, Spacer(1, 1*mm)])) |
|
|
| |
| if sq_list: |
| story.append(Spacer(1, 5*mm)) |
| story.append(HRFlowable(width="100%", thickness=1, color=BORDER, spaceAfter=3)) |
| story.append(Paragraph("【 সৃজনশীল প্রশ্ন 】", section_hdr)) |
| story.append(Paragraph("যেকোনো ২টি প্রশ্নের উত্তর দাও। প্রতিটি প্রশ্নের মান: ১০", note_style)) |
| story.append(Spacer(1, 3*mm)) |
|
|
| mark_map = {"ক": "১", "খ": "২", "গ": "৩", "ঘ": "৪"} |
|
|
| for i, sq in enumerate(sq_list, 1): |
| uddipon = sq.get("uddipon", "") |
| parts = sq.get("parts", {}) |
| sq_topic = sq.get("topic", "") |
|
|
| sq_block = [] |
| sq_block.append(Paragraph(f"<b>প্রশ্ন {i}:</b> [{sq_topic}]", section_hdr)) |
|
|
| if uddipon: |
| sq_block.append(Paragraph(f"<b>উদ্দীপক:</b> {uddipon}", uddipon_style)) |
| sq_block.append(Spacer(1, 2*mm)) |
|
|
| for key in ["ক", "খ", "গ", "ঘ"]: |
| if key in parts: |
| marks = mark_map.get(key, "") |
| sq_block.append(Paragraph( |
| f"<b>{key})</b> {parts[key]} <font color='#e53935'>[{marks} নম্বর]</font>", |
| sq_style |
| )) |
|
|
| sq_container = Table( |
| [[elem] for elem in sq_block], |
| colWidths=[W - 3.6*cm] |
| ) |
| sq_container.setStyle(TableStyle([ |
| ("BACKGROUND", (0,0),(-1,-1), colors.HexColor("#fafafa")), |
| ("TOPPADDING", (0,0),(-1,-1), 6), |
| ("BOTTOMPADDING", (0,0),(-1,-1), 6), |
| ("LEFTPADDING", (0,0),(-1,-1), 10), |
| ("RIGHTPADDING", (0,0),(-1,-1), 10), |
| ("BOX", (0,0),(-1,-1), 1, PRIMARY), |
| ("LINEBELOW", (0,0),(-1,-1), 0.3, BORDER), |
| ])) |
| story.append(KeepTogether([sq_container, Spacer(1, 4*mm)])) |
|
|
| |
| if prediction: |
| story.append(Spacer(1, 3*mm)) |
| story.append(HRFlowable(width="100%", thickness=1, color=GOLD, spaceAfter=3)) |
| story.append(Paragraph("【 পরীক্ষার পূর্বাভাস 】", S("pred_h", fontSize=11, textColor=colors.HexColor("#e65100"), |
| alignment=TA_LEFT, leading=16, spaceBefore=6, spaceAfter=4))) |
| pred_table = Table([[Paragraph(prediction, S("pred_t", fontSize=9, textColor=TEXT_DARK, |
| alignment=TA_JUSTIFY, leading=14, fontName=bn_font))]], |
| colWidths=[W - 3.6*cm]) |
| pred_table.setStyle(TableStyle([ |
| ("BACKGROUND", (0,0),(-1,-1), colors.HexColor("#fff8e1")), |
| ("TOPPADDING", (0,0),(-1,-1), 8), |
| ("BOTTOMPADDING", (0,0),(-1,-1), 8), |
| ("LEFTPADDING", (0,0),(-1,-1), 12), |
| ("RIGHTPADDING", (0,0),(-1,-1), 12), |
| ("BOX", (0,0),(-1,-1), 1, GOLD), |
| ])) |
| story.append(pred_table) |
|
|
| |
| if answer_key: |
| story.append(Spacer(1, 5*mm)) |
| story.append(HRFlowable(width="100%", thickness=1.5, color=PRIMARY, spaceAfter=3)) |
| story.append(Paragraph("【 উত্তরমালা (Answer Key) 】", S("ak_h", fontSize=11, textColor=PRIMARY, |
| alignment=TA_CENTER, leading=16, spaceBefore=4, spaceAfter=4))) |
|
|
| |
| grid_data = [] |
| row = [] |
| for i, ans in enumerate(answer_key, 1): |
| cell_text = f"<b>{i}.</b> {ans}" |
| row.append(Paragraph(cell_text, ans_style)) |
| if len(row) == 5: |
| grid_data.append(row) |
| row = [] |
| if row: |
| while len(row) < 5: |
| row.append(Paragraph("", ans_style)) |
| grid_data.append(row) |
|
|
| if grid_data: |
| ans_table = Table(grid_data, colWidths=[(W-3.6*cm)/5]*5) |
| ans_table.setStyle(TableStyle([ |
| ("BACKGROUND", (0,0),(-1,-1), colors.HexColor("#e8f5e9")), |
| ("TOPPADDING", (0,0),(-1,-1), 4), |
| ("BOTTOMPADDING", (0,0),(-1,-1), 4), |
| ("LEFTPADDING", (0,0),(-1,-1), 6), |
| ("GRID", (0,0),(-1,-1), 0.5, colors.HexColor("#a5d6a7")), |
| ])) |
| story.append(ans_table) |
|
|
| |
| story.append(Spacer(1, 6*mm)) |
| story.append(HRFlowable(width="100%", thickness=0.5, color=BORDER, spaceAfter=3)) |
| story.append(Paragraph( |
| f"Generated by RUBRA Smart Tutor • {now_str} • NCTB 2026 Curriculum • শুভকামনা রইল! ✨", |
| footer_style |
| )) |
|
|
| doc.build(story) |
| return output_path |
|
|
|
|
| async def generate_exam_with_ai( |
| subject: str, |
| class_name: str, |
| topic: str = "", |
| exam_type: str = "mcq", |
| lang: str = "bn", |
| include_srijonshil: bool = False, |
| include_prediction: bool = True, |
| ) -> dict: |
| """ |
| Use AI to generate exam questions in structured JSON format. |
| Returns dict with mcq, srijonshil, answers, prediction. |
| """ |
| bn_label = "বাংলায়" if lang == "bn" else "in English" |
|
|
| srijonshil_instruction = "" |
| if include_srijonshil: |
| srijonshil_instruction = """ |
| এছাড়া ৩টি সৃজনশীল প্রশ্ন তৈরি করো: |
| "srijonshil": [ |
| { |
| "topic": "topic name", |
| "uddipon": "উদ্দীপক (3-5 লাইনের বাস্তব scenario)", |
| "parts": { |
| "ক": "জ্ঞানমূলক প্রশ্ন (১ নম্বর)", |
| "খ": "অনুধাবনমূলক প্রশ্ন (২ নম্বর)", |
| "গ": "প্রয়োগমূলক প্রশ্ন (৩ নম্বর)", |
| "ঘ": "উচ্চতর দক্ষতামূলক প্রশ্ন (৪ নম্বর)" |
| } |
| } |
| ] |
| """ |
|
|
| prompt = f"""তুমি বাংলাদেশের NCTB 2026 পাঠ্যক্রমের উপর ভিত্তি করে পরীক্ষার প্রশ্ন তৈরি করবে। |
| |
| বিষয়: {subject} |
| শ্রেণি: {class_name} |
| {"অধ্যায়/টপিক: " + topic if topic else "সম্পূর্ণ সিলেবাস থেকে"} |
| ভাষা: {bn_label} |
| |
| EXACTLY এই JSON format এ উত্তর দাও (কোনো markdown নয়, শুধু JSON): |
| |
| {{ |
| "mcq": [ |
| {{ |
| "question": "প্রশ্নের টেক্সট", |
| "options": ["ক অপশন", "খ অপশন", "গ অপশন", "ঘ অপশন"], |
| "correct": "ক", |
| "topic": "কোন অধ্যায় থেকে", |
| "explanation": "কেন এটা সঠিক (১ লাইন)" |
| }} |
| ], |
| {'"srijonshil": [],' if not include_srijonshil else srijonshil_instruction} |
| "answers": ["ক", "গ", "খ", ...], |
| "prediction": "এবার বোর্ড পরীক্ষায় কোন topic থেকে প্রশ্ন আসার সম্ভাবনা বেশি এবং কেন (৩-৫ বাক্য)" |
| }} |
| |
| নিয়ম: |
| - ৩০টি MCQ প্রশ্ন দাও (NCTB বই থেকে, conceptual ও কঠিন) |
| - প্রতিটি option যুক্তিযুক্ত distractor হবে |
| - "answers" array তে শুধু সঠিক option এর letter (ক/খ/গ/ঘ) দাও |
| - prediction এ বলো কোন chapter থেকে এবার বেশি আসতে পারে |
| - শুধু JSON দাও, কোনো extra text নয়""" |
|
|
| msgs = [ |
| {"role": "system", "content": TUTOR_ADVANCED_PROMPT}, |
| {"role": "user", "content": prompt} |
| ] |
|
|
| full = "" |
| |
| model_configs = [ |
| (GEMINI_URL, GEMINI_KEY, "gemini-2.5-flash-preview-05-20"), |
| (OR_URL, OR_KEY, "deepseek/deepseek-v3-0324:free"), |
| (GROQ_URL, GROQ_KEY, "llama-3.3-70b-versatile"), |
| (ZAI_CHAT, ZAI_KEY, "glm-4.7-flash"), |
| ] |
|
|
| for url, key, model in model_configs: |
| try: |
| async for tok in stream_llm(msgs, url, key, model, 0.3, 8192): |
| full += tok |
| if full and len(full) > 100: |
| break |
| except Exception as e: |
| log.warning(f"Exam gen model {model} failed: {e}") |
| continue |
|
|
| |
| try: |
| |
| clean = re.sub(r'^```(?:json)?\s*', '', full.strip()) |
| clean = re.sub(r'\s*```$', '', clean) |
| |
| json_match = re.search(r'\{[\s\S]*\}', clean) |
| if json_match: |
| return json.loads(json_match.group(0)) |
| except Exception as e: |
| log.error(f"Exam JSON parse error: {e}\nRaw: {full[:500]}") |
|
|
| return {"mcq": [], "srijonshil": [], "answers": [], "prediction": "", "error": "JSON parse failed"} |
|
|
|
|
| |
|
|
| VISION_PROMPT = """You are RUBRA's Vision Intelligence. You can see and analyze images with exceptional accuracy. |
| |
| For QUESTION PAPERS / EXAM SHEETS: |
| • Read every question carefully |
| • Identify subject, class level, question types |
| • Solve each question step by step |
| • Show complete working for math/science |
| • Explain concepts if asked |
| |
| For DOCUMENTS / TEXT IMAGES: |
| • Extract and transcribe all text accurately |
| • Preserve structure (tables, lists, formulas) |
| • Note any diagrams or charts |
| |
| For GENERAL IMAGES: |
| • Describe what you see in detail |
| • Answer questions about the image |
| • Identify text, objects, people, scenes |
| |
| Language: Match the user's language (Bangla/English/Banglish)""" |
|
|
| |
| |
| |
| |
| |
| |
| async def stream_llm(messages, url, api_key, model, temperature=0.7, max_tokens=4096): |
| |
| headers = {"Content-Type": "application/json"} |
|
|
| if "generativelanguage.googleapis.com" in url: |
| |
| if "?" not in url: |
| url = f"{url}?key={api_key}" |
| headers["Authorization"] = f"Bearer {api_key}" |
| elif "openrouter" in url: |
| headers["Authorization"] = f"Bearer {api_key}" |
| headers["HTTP-Referer"] = "https://rubra.ai" |
| headers["X-Title"] = "RUBRA" |
| else: |
| headers["Authorization"] = f"Bearer {api_key}" |
|
|
| payload = { |
| "model": model, |
| "messages": messages, |
| "stream": True, |
| "max_tokens": max_tokens, |
| "temperature": temperature, |
| } |
|
|
| |
| if "generativelanguage" in url: |
| payload.pop("stream", None) |
| payload["stream"] = True |
|
|
| timeout = aiohttp.ClientTimeout(total=120, connect=15) |
| async with aiohttp.ClientSession(timeout=timeout) as s: |
| async with s.post(url, headers=headers, json=payload) as resp: |
| if resp.status not in (200, 201): |
| body = await resp.text() |
| raise Exception(f"API {resp.status}: {body[:300]}") |
| async for line in resp.content: |
| line = line.decode("utf-8").strip() |
| if not line or line == "data: [DONE]": |
| continue |
| if line.startswith("data: "): |
| line = line[6:] |
| try: |
| delta = json.loads(line)["choices"][0].get("delta", {}) |
| tok = delta.get("content", "") |
| if tok: |
| yield tok |
| except Exception: |
| pass |
|
|
| async def llm(messages,mode="general",max_tokens=4096,temperature=None): |
| configs={ |
| |
| "general": [(ZAI_CHAT, ZAI_KEY, "glm-4.7-flash", 0.7), |
| (GROQ_URL, GROQ_KEY, "llama-3.3-70b-versatile", 0.7)], |
| |
| "coding": [ |
| |
| (ZAI_CODE, ZAI_KEY, "glm-4.7-flash", 0.10), |
| |
| (OR_URL, OR_KEY, "qwen/qwen3-coder-480b-a35b-instruct:free", 0.10), |
| |
| (CEREBRAS_URL,CEREBRAS_KEY, "llama-4-scout-17b-16e-instruct", 0.10), |
| |
| (CEREBRAS_URL,CEREBRAS_KEY, "llama-3.3-70b", 0.10), |
| |
| (GEMINI_URL, GEMINI_KEY, "gemini-2.5-flash-preview-05-20", 0.10), |
| |
| (OR_URL, OR_KEY, "deepseek/deepseek-v3-0324:free", 0.10), |
| |
| (OR_URL, OR_KEY, "qwen/qwen-2.5-coder-32b-instruct:free", 0.10), |
| |
| (GROQ_URL, GROQ_KEY, "meta-llama/llama-4-scout-17b-16e-instruct", 0.15),], |
| |
| "fast": [(ZAI_CHAT, ZAI_KEY, "glm-4.7-flash", 0.8), |
| (GROQ_URL, GROQ_KEY, "meta-llama/llama-4-scout-17b-16e-instruct", 0.8)], |
| |
| "vision": [(GROQ_URL, GROQ_KEY, "meta-llama/llama-4-scout-17b-16e-instruct", 0.5), |
| (OR_URL, OR_KEY, "qwen/qwen2.5-vl-72b-instruct:free", 0.5), |
| (ZAI_CHAT, ZAI_KEY, "glm-4.5v", 0.5)], |
| |
| "reason": [(GROQ_URL, GROQ_KEY, "deepseek-r1-distill-llama-70b", 0.6), |
| (ZAI_CHAT, ZAI_KEY, "glm-4.7-flash", 0.6)], |
| } |
| last=None |
| for url,key,model,temp in configs.get(mode,configs["general"]): |
| try: |
| _temp = temperature if temperature is not None else temp |
| _max = max_tokens |
| async for tok in stream_llm(messages,url,key,model,_temp,_max): yield tok |
| return |
| except Exception as e: last=e; log.warning(f"LLM fail ({model}): {e}") |
| raise Exception(f"All APIs failed. Last: {last}") |
|
|
| def build_msgs(sys_p,hist,user_msg,img=None): |
| msgs=[{"role":"system","content":sys_p}] |
| for h in hist[-14:]: |
| if h.get("role") in("user","assistant") and h.get("content"): |
| msgs.append({"role":h["role"],"content":h["content"]}) |
| if img: |
| msgs.append({"role":"user","content":[ |
| {"type":"image_url","image_url":{"url":f"data:{img['mime']};base64,{img['data']}","detail":"high"}}, |
| {"type":"text","text":user_msg}]}) |
| else: msgs.append({"role":"user","content":user_msg}) |
| return msgs |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import difflib |
|
|
| |
| |
| XML_THINK_SYSTEM = """Use structured reasoning for complex tasks: |
| |
| <thought>Deep analysis — what's needed, edge cases, best approach</thought> |
| <action>Concrete steps — what to compute, build, or research</action> |
| <observation>Results — what was found, patterns, insights</observation> |
| <final_response>Complete polished answer for the user</final_response> |
| |
| For simple queries: skip XML, respond directly.""" |
|
|
| def parse_xml_response(raw: str) -> str: |
| m = re.search(r'<final_response>([\s\S]*?)</final_response>', raw, re.IGNORECASE) |
| if m: return m.group(1).strip() |
| clean = re.sub(r'<(?:thought|action|observation)[^>]*>[\s\S]*?</(?:thought|action|observation)>','',raw,flags=re.IGNORECASE) |
| return clean.strip() or raw.strip() |
|
|
| def extract_thought_chain(raw: str) -> list: |
| chain = [] |
| for tag in ["thought","action","observation"]: |
| m = re.search(rf'<{tag}>([\s\S]*?)</{tag}>', raw, re.IGNORECASE) |
| if m: chain.append({"step":tag,"content":m.group(1).strip()[:400]}) |
| return chain |
|
|
|
|
| |
| def classify_coding_task(msg: str) -> dict: |
| """Classify coding request into skill domains for targeted prompt injection.""" |
| lower = msg.lower() |
| return { |
| "is_frontend": bool(re.search( |
| r'\b(html|css|react|vue|svelte|nextjs|tailwind|landing.?page|dashboard|' |
| r'website|ui\b|interface|component|frontend|design|glassmorphism|dark.?mode|' |
| r'navbar|hero|card|button|form|modal|animation|responsive|figma|wireframe|' |
| r'typography|color.?palette|layout|grid|flex|sass|styled.?components)\b', lower)), |
| "is_game": bool(re.search( |
| r'\b(game\b|godot|unity|unreal|platformer|rpg|shooter|puzzle|arcade|' |
| r'sprite|tilemap|physics|game.?loop|player|enemy|score|level|' |
| r'gdscript|blueprint|game.?dev|gdd|core.?loop|collision|inventory|npc)\b', lower)), |
| "is_backend": bool(re.search( |
| r'\b(api\b|backend|server|database|fastapi|flask|django|express|node\.?js|' |
| r'sql\b|postgres|mysql|mongodb|redis|auth|jwt|endpoint|crud|rest\b|' |
| r'graphql|docker|microservice|queue|worker|algorithm|data.?structure)\b', lower)), |
| "is_mobile": bool(re.search( |
| r'\b(react.?native|flutter|android|ios|expo|mobile.?app|swift|kotlin)\b', lower)), |
| "is_devops": bool(re.search( |
| r'\b(docker|kubernetes|ci.?cd|github.?action|nginx|deploy|terraform|bash.?script)\b', lower)), |
| "is_data": bool(re.search( |
| r'\b(machine.?learning|pytorch|tensorflow|pandas|numpy|sklearn|' |
| r'data.?analysis|matplotlib|model.?training|jupyter)\b', lower)), |
| "needs_architect": bool(re.search( |
| r'\b(complex|architecture|system.?design|full.?stack|entire|complete.?app|' |
| r'from.?scratch|production|scalable|enterprise|best.?way|design.?pattern)\b', lower)), |
| "is_debug": bool(re.search( |
| r'\b(bug|error|fix|debug|not.?working|broken|issue|exception|traceback|crash)\b', lower)), |
| "is_refactor": bool(re.search( |
| r'\b(refactor|optimize|improve|clean.?up|rewrite|performance|better.?way)\b', lower)), |
| } |
|
|
|
|
| |
| def build_architect_prompt(msg: str, task: dict) -> str: |
| """ |
| Aider-inspired: Architect mode first plans the solution, |
| Editor mode then writes the actual code. |
| For complex tasks, we embed both in one prompt. |
| """ |
| if not task["needs_architect"]: |
| return "" |
| return """ |
| ═══ ARCHITECT MODE (Plan before coding) ═══ |
| For this complex task, first produce a brief plan: |
| 1. What files/components need to be created? |
| 2. What is the data flow / architecture? |
| 3. What are the key technical decisions? |
| 4. Any risks or edge cases to handle? |
| |
| Then immediately proceed to EDITOR MODE — write all the code. |
| Do NOT stop after the plan. Plan is just internal prep. |
| ═══ EDITOR MODE (Write complete code after planning) ═══ |
| """ |
|
|
|
|
| |
| def trim_prompt_smart(sys_p: str, max_chars: int = 13000) -> str: |
| """ |
| Cline-inspired context management: |
| Preserve critical sections, trim repetitive/decorative content. |
| Priority: Core rules > Skill content > Examples > Checklists |
| """ |
| if len(sys_p) <= max_chars: |
| return sys_p |
|
|
| |
| sys_p = re.sub( |
| r'═+\s*PRE-DELIVERY CHECKLIST.*?(?=═{3}|\Z)', |
| '[✓ Apply: cursor:pointer, hover states, responsive 375-1440px, WCAG AA contrast, alt text]\n', |
| sys_p, flags=re.DOTALL |
| ) |
|
|
| if len(sys_p) <= max_chars: |
| return sys_p |
|
|
| |
| sys_p = re.sub( |
| r'(LAYOUT \(Rules 1-20\):[\s\S]{0,200})(\d+\. .+\n){5,}', |
| r'\1[...layout rules apply]\n', |
| sys_p |
| ) |
|
|
| if len(sys_p) <= max_chars: |
| return sys_p |
|
|
| |
| return sys_p[:max_chars] + "\n[...apply remaining best practices from training]" |
|
|
|
|
| |
| def build_hermes_prompt(msg: str, lang: str = "en") -> str: |
| """ |
| OpenCode-inspired agent hierarchy: |
| - BUILD agent: full access, writes code |
| - PLAN agent: read-only analysis (embedded as thinking phase) |
| - GENERAL subagent: complex search/research tasks |
| |
| All three modes are embedded contextually based on task type. |
| """ |
| task = classify_coding_task(msg) |
| li = lang_instr(lang) |
|
|
| |
| mode_label = "ARCHITECT+EDITOR" if task["needs_architect"] else ( |
| "DEBUG" if task["is_debug"] else ( |
| "REFACTOR" if task["is_refactor"] else "BUILD")) |
|
|
| prompt = f"""You are RUBRA's Hermes Core Engine — world-class coding intelligence. |
| Mode: {mode_label} |
| |
| ═══ CORE MISSION ═══ |
| Produce complete, production-ready code that works on the first run. |
| No truncation. No placeholders. No TODOs. No "..." shortcuts. |
| |
| ═══ CODEACT LOOP (Think → Act → Verify) ═══ |
| Before writing code: |
| 1. THINK: What exactly is needed? Best approach? Edge cases? |
| 2. ACT: Write complete, working code |
| 3. VERIFY: Mentally run it. Any bugs? Fix them before outputting. |
| |
| ═══ ABSOLUTE RULES ═══ |
| ✗ NEVER: "// rest of code...", "# TODO", "...", "[continues]" |
| ✗ NEVER: Truncate a function, class, or file mid-way |
| ✗ NEVER: Placeholder logic or mock implementations |
| ✓ ALWAYS: Complete every bracket, tag, function, class |
| ✓ ALWAYS: Real working code only |
| ✓ IF LONG: Finish current block → write <!-- RUBRA_CONTINUE --> |
| The system auto-requests continuation. Just mark it. |
| |
| {build_architect_prompt(msg, task)} |
| """ |
|
|
| |
|
|
| |
| if task["is_frontend"] or (not any([ |
| task["is_backend"], task["is_game"], |
| task["is_mobile"], task["is_devops"], task["is_data"] |
| ])): |
| prompt += f""" |
| ═══ UI/UX PRO MAX SKILL — MANDATORY ═══ |
| {UIUX_PRO_MAX} |
| |
| DESIGN SYSTEM (apply before any UI code): |
| → Identify product type → match 161 rules → select style from 67 |
| → Apply color palette + typography from 57 pairings |
| → Follow all 99 UX guidelines strictly |
| → Run pre-delivery checklist |
| |
| HARD UI RULES: |
| ✗ No emoji as icons → Lucide/Heroicons SVG only |
| ✗ No placeholder text in rendered UI |
| ✗ No missing cursor:pointer or hover states |
| ✓ Mobile-first: 375→768→1024→1440px |
| ✓ Smooth transitions 150-300ms |
| ✓ Loading + error + empty states always |
| ✓ WCAG AA contrast minimum |
| """ |
|
|
| |
| if task["is_game"]: |
| prompt += f""" |
| ═══ GAME DEV STUDIO SKILL — MANDATORY ═══ |
| {GAME_DEV_STUDIO} |
| |
| GAME RULES: |
| ✓ Core loop design FIRST, then code |
| ✓ No magic numbers → constants/config |
| ✓ Delta time on ALL movement |
| ✓ State machine for entities |
| ✓ Object pooling for spawned objects |
| ✓ Separate: Logic / Render / Input |
| """ |
|
|
| |
| if task["is_backend"]: |
| prompt += """ |
| ═══ BACKEND EXCELLENCE SKILL ═══ |
| Architecture: Routes → Controllers → Services → Models/DB |
| |
| MANDATORY: |
| ✓ Type hints (Python) / TypeScript strict |
| ✓ Input validation on every endpoint (Pydantic/Zod) |
| ✓ Error handling on every async operation |
| ✓ Parameterized SQL queries only |
| ✓ bcrypt/argon2 for passwords |
| ✓ Environment variables for all secrets |
| ✓ Rate limiting on public endpoints |
| ✓ Database indexes on queried fields |
| GENERATE: All routes + models + .env.example + setup README |
| """ |
|
|
| |
| if task["is_mobile"]: |
| prompt += """ |
| ═══ MOBILE SKILL ═══ |
| ✓ 44pt/48dp minimum touch targets |
| ✓ Safe area handling |
| ✓ Platform-specific code minimized |
| ✓ Navigation: React Navigation v6 / GoRouter |
| ✓ State: Zustand / Riverpod |
| """ |
|
|
| |
| if task["is_debug"]: |
| prompt += """ |
| ═══ DEBUG MODE ═══ |
| 1. Identify the exact error/symptom |
| 2. Trace the root cause (not just symptoms) |
| 3. Provide the minimal correct fix |
| 4. Explain WHY it was broken |
| 5. Show before/after if helpful |
| """ |
|
|
| |
| if task["is_refactor"]: |
| prompt += """ |
| ═══ REFACTOR MODE ═══ |
| 1. Analyze current code structure |
| 2. Identify: duplication / complexity / performance issues |
| 3. Apply: SOLID principles, DRY, clean architecture |
| 4. Preserve: all existing functionality |
| 5. Show: what changed and why |
| """ |
|
|
| |
| if li: |
| prompt += f"\n\n{li}\n(Code in English always. Explanations in user's language.)" |
|
|
| return prompt |
|
|
|
|
| |
| |
| |
| CODING_MODEL_CASCADE = [ |
| |
| {"url": OR_URL, "key": OR_KEY, |
| "model": "deepseek/deepseek-v3-0324:free", |
| "temp": 0.05, "max_tokens": 8192, "label": "DeepSeek-V3"}, |
|
|
| {"url": OR_URL, "key": OR_KEY, |
| "model": "qwen/qwen3-coder-480b-a35b-instruct:free", |
| "temp": 0.05, "max_tokens": 8192, "label": "Qwen3-Coder-480B"}, |
|
|
| {"url": GEMINI_URL, "key": GEMINI_KEY, |
| "model": "gemini-2.5-flash-preview-05-20", |
| "temp": 0.05, "max_tokens": 8192, "label": "Gemini-2.5-Flash"}, |
|
|
| |
| {"url": ZAI_CODE, "key": ZAI_KEY, |
| "model": "glm-4.7-flash", |
| "temp": 0.10, "max_tokens": 8192, "label": "GLM-4.7-Code"}, |
|
|
| {"url": CEREBRAS_URL, "key": CEREBRAS_KEY, |
| "model": "llama-4-scout-17b-16e-instruct", |
| "temp": 0.10, "max_tokens": 8192, "label": "Cerebras-Scout"}, |
|
|
| {"url": CEREBRAS_URL, "key": CEREBRAS_KEY, |
| "model": "llama-3.3-70b", |
| "temp": 0.10, "max_tokens": 8192, "label": "Cerebras-70B"}, |
|
|
| |
| {"url": OR_URL, "key": OR_KEY, |
| "model": "qwen/qwen-2.5-coder-32b-instruct:free", |
| "temp": 0.15, "max_tokens": 8192, "label": "Qwen-Coder-32B"}, |
|
|
| {"url": GROQ_URL, "key": GROQ_KEY, |
| "model": "meta-llama/llama-4-scout-17b-16e-instruct", |
| "temp": 0.15, "max_tokens": 8192, "label": "Groq-Scout"}, |
|
|
| {"url": GROQ_URL, "key": GROQ_KEY, |
| "model": "llama-3.3-70b-versatile", |
| "temp": 0.15, "max_tokens": 4096, "label": "Groq-70B"}, |
| ] |
|
|
| |
| _CASCADE_TRIGGERS = { |
| "401","403","404", |
| "413","429", |
| "500","502","503", |
| "rate limit","quota", |
| "capacity","overloaded", |
| "too large","token", |
| "user not found", |
| "model not found", |
| "timeout","timed out", |
| } |
|
|
| async def hermes_stream(messages: list, task: dict): |
| """ |
| RUBRA Core Engine cascade. |
| OpenCode-inspired: provider-agnostic, best-first, auto-fallback. |
| Cline-inspired: context-aware, task-type routing. |
| """ |
| last_err = None |
| tried = [] |
|
|
| for cfg in CODING_MODEL_CASCADE: |
| tried.append(cfg["label"]) |
| try: |
| log.info(f"🔧 RUBRA Core → {cfg['label']}") |
| got_tokens = False |
| async for tok in stream_llm( |
| messages, cfg["url"], cfg["key"], |
| cfg["model"], cfg["temp"], cfg["max_tokens"] |
| ): |
| got_tokens = True |
| yield tok |
| if got_tokens: |
| log.info(f"✅ {cfg['label']} completed") |
| return |
| except Exception as e: |
| last_err = e |
| err_str = str(e).lower() |
| should_cascade = any(t in err_str for t in _CASCADE_TRIGGERS) |
| if should_cascade: |
| log.warning(f"⚡ {cfg['label']} → cascade ({str(e)[:70]})") |
| await asyncio.sleep(0.2) |
| continue |
| else: |
| |
| log.error(f"❌ {cfg['label']} hard fail: {e}") |
| raise e |
|
|
| raise Exception( |
| f"RUBRA Core Engine exhausted ({len(tried)} models tried). " |
| f"Last: {last_err}" |
| ) |
|
|
|
|
| |
| async def skill_generate_and_save(skill_name: str, task_desc: str) -> str: |
| prompt = f"""Generate a complete Python function for: {task_desc} |
| Function name: run_{skill_name}(input_data: dict) -> dict |
| Requirements: type hints, docstring, error handling, returns {{"result":..,"success":bool,"error":str|None}}""" |
| msgs = [{"role":"system","content":"Output ONLY pure Python code, no markdown."}, |
| {"role":"user","content":prompt}] |
| code = "" |
| async for tok in llm(msgs,"coding"): code += tok |
| code = re.sub(r'^```python','',code.strip()); code = re.sub(r'```$','',code.strip()) |
| (SKILLS_DIR/f"{skill_name}.py").write_text(code) |
| return code |
|
|
| def skill_load(name: str) -> str: |
| f = SKILLS_DIR/f"{name}.py"; return f.read_text() if f.exists() else None |
|
|
| def skill_list() -> list: |
| return [f.stem for f in SKILLS_DIR.glob("*.py")] |
| |
| |
| |
| class GeneralAgent: |
| name="GeneralAgent" |
| async def run(self,msg,hist,sid="",lang="en",img=None): |
| li=lang_instr(lang); tool_ctx=""; rag_ctx="" |
| |
| sess=session_load(sid); sess["preferred_lang"]=lang |
| session_update(sid,"user_intent_history",{"msg":msg[:120],"ts":time.time()}) |
| |
| if not img and re.search(r"\b(what is|who is|how does|explain|define|history|overview|what are)\b",msg,re.IGNORECASE): |
| q=re.sub(r"\b(what is|who is|how does|explain|define|tell me|about|the|a|an|please)\b","",msg,flags=re.IGNORECASE).strip()[:60] |
| if len(q)>3: |
| page=tool_wikipedia(q) |
| if page: tool_ctx=f"[WIKIPEDIA: {page['title']}]\n{page['text']}" |
| |
| if re.search(r"\b(latest|recent|trending|news|today|2025|2026|current)\b",msg,re.IGNORECASE): |
| items=feed_get(limit=5) |
| if items: |
| feed_ctx="[LIVE KNOWLEDGE]\n"+"\n".join(f"• {f['title']} ({f['source']})" for f in items[:4]) |
| tool_ctx=feed_ctx+("\n\n"+tool_ctx if tool_ctx else "") |
| |
| hits=rag_search(msg,limit=3) |
| if hits: rag_ctx="\n".join(f"[{s}:{t}]\n{c}" for _,t,c,s in hits[:2]) |
| |
| topic_ctx="" |
| if sess.get("topic_memory"): |
| recent=list(sess["topic_memory"].items())[-3:] |
| topic_ctx="[SESSION CONTEXT]\n"+"\n".join(f"• {k}" for k,v in recent) |
| sinthia_extra = SINTHIA_SYSTEM_EXTRA if is_sinthia(msg) else "" |
| parts=[RUBRA_CORE, sinthia_extra, XML_THINK_SYSTEM,"\n[DEEP REASONING] Use XML chain. Show steps."] |
| if li: parts.append(li) |
| if tool_ctx: parts.append(tool_ctx) |
| if rag_ctx: parts.append(f"[KNOWLEDGE]\n{rag_ctx}") |
| if topic_ctx: parts.append(topic_ctx) |
| msgs=build_msgs("\n\n".join(parts),hist,msg,img) |
| full_raw="" |
| try: |
| mode="vision" if img else "general" |
| async for tok in llm(msgs,mode): |
| full_raw+=tok; yield {"type":"token","content":tok} |
| chain=extract_thought_chain(full_raw) |
| if chain: session_update(sid,"thought_chain",{"query":msg[:80],"chain":chain,"ts":time.time()}) |
| except Exception as e: yield {"type":"error","message":str(e)[:200]} |
|
|
| class CodingAgent: |
| name = "CodingAgent" |
|
|
| async def run(self, msg, hist, sid="", lang="en", img=None): |
| task = classify_coding_task(msg) |
|
|
| |
| sys_p = build_hermes_prompt(msg, lang) |
|
|
| |
| if task["is_frontend"]: |
| try: |
| m = re.search( |
| r'for\s+(?:a\s+|my\s+)?([a-zA-Z\s]{3,40}?)(?:\s+(?:app|website|platform|tool|page|dashboard))?(?:\s|$)', |
| msg, re.IGNORECASE) |
| proj = m.group(1).strip() if m else "" |
| sys_p += f"\n\n[AUTO DESIGN SYSTEM]\n{uiux_design_system(msg, proj)}" |
| except Exception: |
| pass |
|
|
| |
| hits = rag_search(msg, limit=2) |
| if hits: |
| sys_p += "\n\n[CONTEXT]\n" + "\n".join( |
| f"• {t}: {c[:80]}" for _, t, c, s in hits[:2]) |
|
|
| |
| sys_p = trim_prompt_smart(sys_p, max_chars=13000) |
|
|
| |
| if msg.startswith("[RUBRA_CONTINUE]"): |
| last = msg.replace("[RUBRA_CONTINUE]", "").strip() |
| msg = f"Continue EXACTLY from here — no intro, no repetition:\n...{last}" |
|
|
| if img: |
| msg = f"[Screenshot/Image provided]\n{msg}" |
|
|
| msgs = build_msgs(sys_p, hist, msg, img) |
|
|
| try: |
| if img: |
| async for tok in llm(msgs, "vision", max_tokens=8192, temperature=0.1): |
| yield {"type": "token", "content": tok} |
| else: |
| async for tok in hermes_stream(msgs, task): |
| yield {"type": "token", "content": tok} |
| except Exception as e: |
| yield {"type": "error", "message": str(e)[:200]} |
| |
| class SearchAgent: |
| name="SearchAgent" |
| async def run(self,msg,hist,sid="",lang="en",img=None): |
| lower=msg.lower(); li=lang_instr(lang); tool_ctx="" |
| if re.search(r"\b(weather|temperature|forecast|rain|cold|hot|humid|wind)\b",lower): |
| city="Dhaka" |
| for c in CITY_COORDS: |
| if c in lower: city=c.title(); break |
| m=re.search(r"weather\s+(?:in\s+)?([a-z\s]{3,20})(?:\?|$|\.|\!)",lower) |
| if m: city=m.group(1).strip().title() |
| w=tool_weather(city) |
| if w: |
| tool_ctx=f"[LIVE WEATHER — {w['city']}]\nTemp: {w['temp']}°C (feels {w['feels']}°C) | {w['condition']} | Humidity: {w['humidity']}% | Wind: {w['wind']} km/h" |
| yield {"type":"tool_result","tool":"weather","data":w} |
| elif re.search(r"\b(bitcoin|ethereum|btc|eth|solana|crypto|coin|binance)\b",lower): |
| cm={"btc":"bitcoin","eth":"ethereum","sol":"solana","bnb":"binancecoin"} |
| found=[cm.get(k,k) for k in cm if k in lower] |
| d=tool_crypto(",".join(found or ["bitcoin","ethereum","solana"])) |
| if d: |
| lines=[f"{'📈' if i.get('usd_24h_change',0)>=0 else '📉'} {c.capitalize()}: ${i.get('usd',0):,.2f} ({i.get('usd_24h_change',0):+.2f}%)" for c,i in d.items()] |
| tool_ctx="[LIVE CRYPTO]\n"+"\n".join(lines) |
| yield {"type":"tool_result","tool":"crypto","data":d} |
| elif re.search(r"\b(exchange rate|forex|usd to|eur to|taka|bdt|currency)\b",lower): |
| bases=re.findall(r"\b(USD|EUR|GBP|JPY|BDT|INR|CAD|AUD|CNY)\b",msg.upper()) |
| d=tool_currency(bases[0] if bases else "USD") |
| if d: |
| lines=[f"1 {d['base']} = {r} {c}" for c,r in list(d["rates"].items())[:8]] |
| tool_ctx=f"[LIVE RATES — {d['base']}]\n"+"\n".join(lines) |
| yield {"type":"tool_result","tool":"currency","data":d} |
| elif re.search(r"\b(latest news|trending|what.{0,10}happening|current events|today)\b",lower): |
| cat=None |
| if re.search(r"\b(tech|ai|software)\b",lower): cat="tech" |
| elif re.search(r"\b(bangladesh|dhaka)\b",lower): cat="bangladesh" |
| items=feed_get(category=cat,limit=8) |
| if items: |
| lines=[f"• **{f['title']}** — _{f['source']}_" for f in items] |
| tool_ctx="[LIVE NEWS]\n"+"\n".join(lines) |
| yield {"type":"tool_result","tool":"news","count":len(items)} |
| elif re.search(r"\b(2025 book|2026 book|new book|latest book|recent book)\b",lower): |
| q=re.sub(r"\b(2025|2026|new|latest|recent|book|recommend|best|read|novel)\b","",lower).strip()[:50] |
| books=tool_books_2026(q) |
| if books: |
| lines=[f"📚 **{b['title']}** ({b.get('year','?')}) — {', '.join(b.get('authors',b.get('author_name',[]))[:2])}" for b in books] |
| tool_ctx="[RECENT BOOKS 2024-2026]\n"+"\n".join(lines) |
| elif re.search(r"\b(recommend.{0,10}book|best books|reading list)\b",lower): |
| books=tool_books(re.sub(r"\b(recommend|book|about|best|read)\b","",lower).strip()[:50]) |
| if books: |
| tool_ctx="[BOOKS]\n"+"\n".join(f"📚 {b['title']} ({b.get('year','?')}) — {', '.join(b['authors'][:2])}" for b in books) |
| elif re.search(r"\b(research papers?|arxiv|academic|scientific)\b",lower): |
| q=re.sub(r"\b(research|papers?|arxiv|find|latest)\b","",lower).strip()[:70] |
| papers=tool_arxiv(q or msg,n=4) |
| if papers: |
| tool_ctx="[ARXIV]\n"+"\n\n".join(f"• **{p['title']}** — {', '.join(p['authors'][:2])}\n {p['summary'][:200]}…" for p in papers) |
| else: |
| q=re.sub(r"\b(who is|what is|tell me about|history of|the|a|an)\b","",lower).strip()[:60] |
| page=tool_wikipedia(q or msg) |
| if page: |
| tool_ctx=f"[WIKIPEDIA: {page['title']}]\n{page['text']}" |
| yield {"type":"tool_result","tool":"wikipedia","title":page["title"]} |
| parts=[RUBRA_CORE,"\n[LIVE SEARCH MODE] Answer directly using retrieved data."] |
| if li: parts.append(li) |
| if tool_ctx: parts.append(tool_ctx) |
| msgs=build_msgs("\n\n".join(parts),hist,msg) |
| try: |
| async for tok in llm(msgs,"general"): yield {"type":"token","content":tok} |
| except Exception as e: yield {"type":"error","message":str(e)[:200]} |
|
|
| class SmartTutorAgent: |
| name = "SmartTutorAgent" |
|
|
| async def run(self, msg, hist, sid="", lang="en", img=None): |
| li = lang_instr(lang) |
| lower = msg.lower() |
|
|
| |
| wants_exam = bool(re.search( |
| r'\b(exam|mcq|পরীক্ষা|প্রশ্ন তৈরি|question paper|সৃজনশীল|model test|pdf)\b', |
| lower, re.IGNORECASE)) |
| wants_pdf = bool(re.search(r'\b(pdf|download|ডাউনলোড)\b', lower, re.IGNORECASE)) |
| wants_prediction = bool(re.search( |
| r'\b(prediction|আসতে পারে|আসবে|পূর্বাভাস|কোন প্রশ্ন|কি আসবে)\b', |
| lower, re.IGNORECASE)) |
|
|
| |
| sys_p = TUTOR_ADVANCED_PROMPT + "\n\n" + TUTOR_PROMPT |
| if li: |
| sys_p += f"\n\n{li}" |
|
|
| |
| for class_key, data in NCTB_2026.items(): |
| if any(kw in lower for kw in [class_key.lower().replace("_"," "), class_key.lower()]): |
| sys_p += f"\n\n[NCTB 2026 — {class_key}]\n{json.dumps(data, ensure_ascii=False, indent=2)[:1200]}" |
| break |
|
|
| |
| hits = rag_search(msg, limit=3) |
| if hits: |
| sys_p += "\n\n[STUDY MATERIAL]\n" + "\n".join(f"[{s}:{t}]\n{c}" for _,t,c,s in hits) |
|
|
| |
| if wants_prediction and not img: |
| sys_p += """ |
| \n[EXAM PREDICTION MODE] |
| Board পরীক্ষার pattern বিশ্লেষণ করে বলো: |
| - কোন chapter থেকে বেশি আসে |
| - কোন type এর প্রশ্ন বেশি আসে |
| - এবার কী পড়া উচিত |
| Specific ও practical advice দাও।""" |
|
|
| msgs = build_msgs(sys_p, hist, msg, img) |
| try: |
| mode = "vision" if img else "general" |
| async for tok in llm(msgs, mode): |
| yield {"type": "token", "content": tok} |
| except Exception as e: |
| yield {"type": "error", "message": str(e)[:200]} |
| if li: sys_p+=f"\n\n{li}" |
| |
| for class_key,data in NCTB_2026.items(): |
| if any(kw in msg.lower() for kw in [class_key.lower().replace("_"," "),class_key.lower()]): |
| sys_p+=f"\n\n[NCTB 2026 — {class_key}]\n{json.dumps(data,ensure_ascii=False,indent=2)[:1000]}" |
| break |
| hits=rag_search(msg,limit=2) |
| if hits: sys_p+="\n\n[STUDY MATERIAL]\n"+"\n".join(f"[{s}:{t}]\n{c}" for _,t,c,s in hits) |
| msgs=build_msgs(sys_p,hist,msg,img) |
| try: |
| mode="vision" if img else "general" |
| async for tok in llm(msgs,mode): yield {"type":"token","content":tok} |
| except Exception as e: yield {"type":"error","message":str(e)[:200]} |
|
|
| class VisionAgent: |
| """Advanced multi-model vision: OCR.space → Groq Vision → Qwen2.5-VL""" |
| name="VisionAgent" |
| async def run(self,msg,hist,sid="",lang="en",img=None): |
| li=lang_instr(lang) |
| sys_p=VISION_PROMPT |
| if li: sys_p+=f"\n\n{li}" |
| if not img: |
| yield {"type":"error","message":"No image provided"}; return |
| |
| msgs=build_msgs(sys_p,hist,msg,img) |
| try: |
| async for tok in llm(msgs,"vision"): yield {"type":"token","content":tok} |
| except Exception as e: |
| |
| log.warning(f"Vision LLM failed: {e}, falling back to OCR") |
| img_bytes=base64.b64decode(img["data"]) |
| ocr_text=ocr_image_space(img_bytes,img["mime"]) |
| if not ocr_text: ocr_text=ocr_image_tesseract(img_bytes) |
| if ocr_text: |
| fallback_sys=VISION_PROMPT+f"\n\n[OCR EXTRACTED TEXT FROM IMAGE]\n{ocr_text}\n[/OCR]" |
| if li: fallback_sys+=f"\n\n{li}" |
| msgs2=build_msgs(fallback_sys,hist,msg or "Analyze this content") |
| try: |
| async for tok in llm(msgs2,"general"): yield {"type":"token","content":tok} |
| except Exception as e2: yield {"type":"error","message":str(e2)[:200]} |
| else: |
| yield {"type":"error","message":"Could not process image. Try a clearer photo."} |
|
|
| class FileAgent: |
| name="FileAgent" |
| def _read(self,fp): |
| ext=fp.suffix.lower() |
| try: |
| if ext==".pdf": return pdf_to_text(fp) |
| if ext in(".xlsx",".xls"): |
| try: |
| import openpyxl; wb=openpyxl.load_workbook(fp,read_only=True,data_only=True) |
| out=[] |
| for name in wb.sheetnames[:4]: |
| ws=wb[name]; out.append(f"## Sheet: {name}") |
| for row in ws.iter_rows(max_row=80,values_only=True): |
| out.append(" | ".join(str(c) if c is not None else "" for c in row)) |
| return "\n".join(out)[:12000] |
| except ImportError: return "[openpyxl not installed]" |
| if ext==".csv": |
| import csv |
| with open(fp,"r",encoding="utf-8",errors="replace") as f: |
| return "\n".join(", ".join(r) for r in csv.reader(f))[:10000] |
| if ext in(".docx",".doc"): |
| try: |
| import docx; doc=docx.Document(str(fp)) |
| return "\n".join(p.text for p in doc.paragraphs if p.text.strip())[:14000] |
| except ImportError: return "[python-docx not installed]" |
| return fp.read_text(encoding="utf-8",errors="replace")[:14000] |
| except Exception as e: return f"[Read error: {e}]" |
|
|
| async def analyze(self,fp,fname,question,sid="",lang="en",img_data=None): |
| li=lang_instr(lang); ext=fp.suffix.lower() |
| sys_p=RUBRA_CORE+"\n\n[FILE ANALYSIS] Extract insights. Answer question clearly. Use structure." |
| if li: sys_p+=f"\n\n{li}" |
| if ext in IMAGE_EXTS or img_data: |
| b64,mime=to_base64(fp) if not img_data else (img_data["data"],img_data["mime"]) |
| img_d={"data":b64,"mime":mime} |
| |
| vis=VisionAgent() |
| async for evt in vis.run(question or f"Analyze: {fname}",[],sid,lang,img_d): |
| yield evt |
| return |
| content=self._read(fp) |
| sys_p+=f"\n\n[FILE: {fname}]\n{content}\n[/FILE]" |
| msgs=build_msgs(sys_p,[],question or f"Analyze: {fname}") |
| try: |
| async for tok in llm(msgs,"general"): yield {"type":"token","content":tok} |
| except Exception as e: yield {"type":"error","message":str(e)[:200]} |
|
|
| async def run(self,msg,hist,sid="",lang="en",img=None): |
| yield {"type":"error","message":"Use /api/upload for file analysis"} |
|
|
| class FastChatAgent: |
| name="FastChatAgent" |
| async def run(self,msg,hist,sid="",lang="en",img=None): |
| li=lang_instr(lang) |
| sys_p=RUBRA_CORE+"\n\n[CONVERSATIONAL] Warm, natural, concise. Match user's energy." |
| if li: sys_p+=f"\n\n{li}" |
| msgs=build_msgs(sys_p,hist,msg) |
| try: |
| async for tok in llm(msgs,"fast"): yield {"type":"token","content":tok} |
| except Exception as e: yield {"type":"error","message":str(e)[:200]} |
|
|
| |
| |
| |
| INTENT_MAP=[ |
| |
| (r'https?://[^\s]{10,}', "browse", BrowseAgent), |
| (r'\b(browse|scrape|navigate to|open url|visit site|fetch page)\b', "browse", BrowseAgent), |
| (r'\b(who is|profile of|linkedin|contact info|email of|phone of)\b.{3,}', "browse", BrowseAgent), |
| (r'\b(latest post|recent article|find contact|professional details|company info)\b',"browse", BrowseAgent), |
| (r"\b(make|build|create|design).{0,20}(game|platformer|rpg|shooter|puzzle|arcade|mobile game)\b","game",CodingAgent), |
| (r"\b(godot|unity|unreal|gdscript|blueprint|game engine|game dev)\b","game",CodingAgent), |
| (r"\b(game design|gdd|game mechanic|core loop|level design|sprite|tilemap|physics2d)\b","game",CodingAgent), |
| |
| (r"\b(2025|2026).{0,10}(book|novel|read)","books",SearchAgent), |
| |
| (r"\b(class [0-9]+).{0,20}(math|science|physics|chemistry|biology|solve|korao|bujhao|shekao)","tutor",SmartTutorAgent), |
| (r"\b(ssc|hsc|jsc).{0,20}(bujhao|explain|solve|shekho)","tutor",SmartTutorAgent), |
| (r"\b(weather|temperature|forecast|rain|cold|hot|humid|wind|climate)\b","weather",SearchAgent), |
| (r"\b(bitcoin|ethereum|btc|eth|solana|crypto|coin price|binance|bnb)\b","crypto",SearchAgent), |
| (r"\b(exchange rate|forex|usd to|eur to|taka|bdt|currency conversion)\b","currency",SearchAgent), |
| (r"\b(latest news|what.{0,10}happening|trending|current events|breaking)\b","news",SearchAgent), |
| (r"\b(research papers?|arxiv|academic|scientific|peer.?reviewed)\b","research",SearchAgent), |
| (r"\b(2025 book|2026 book|new book|latest book|recent book)\b","books",SearchAgent), |
| (r"\b(recommend.{0,10}book|best books|reading list)\b","books",SearchAgent), |
| (r"\b(analyze|read|summarize|extract)\b.{0,20}\b(file|pdf|excel|csv|doc)\b","file",FileAgent), |
| |
| (r"\b(write|create|build|implement|generate|make)\b.{0,30}\b(python|javascript|typescript|rust|go|java|html|css|sql|bash|react|node|api|flask|django|fastapi|express|vue|svelte|nextjs|tailwind|website|app|landing page|dashboard|component)\b","code",CodingAgent), |
| (r"\b(debug|fix|refactor|optimize|review)\b.{0,20}\b(code|function|script|bug|error|exception)\b","code",CodingAgent), |
| (r"```|def |class |const |let |var |import .* from|from .* import","code",CodingAgent), |
| (r"\b(algorithm|data structure|sorting|recursion|dynamic programming|api endpoint)\b","code",CodingAgent), |
| (r"\b(website|web app|mobile app|landing page|dashboard|ui component|frontend|backend)\b","code",CodingAgent), |
| |
| (r"\b(ssc|hsc|jsc|psc|board exam|creative question|সৃজনশীল|বহুনির্বাচনি)\b","tutor",SmartTutorAgent), |
| (r"\b(class [0-9]|class six|seven|eight|nine|ten|eleven|twelve)\b","tutor",SmartTutorAgent), |
| (r"\b(প্রশ্ন|উত্তর|পড়া|শেখা|বোঝা|গণিত|বিজ্ঞান|বাংলা|ইতিহাস|ভূগোল|রসায়ন|পদার্থ)\b","tutor",SmartTutorAgent), |
| (r"\b(solve|bujhao|shekao|explain).{0,30}(math|science|physics|chemistry|biology|bangla|history)\b","tutor",SmartTutorAgent), |
| (r"\b(question paper|exam paper|model test|practice exam|nctb)\b","tutor",SmartTutorAgent), |
| |
| (r"\b(explain|analyze|compare|evaluate|how does|why does|difference between|what causes)\b","reasoning",GeneralAgent), |
| (r"\b(neural|machine.?learning|deep.?learning|transformer|llm|ai model|quantum|consciousness)\b","reasoning",GeneralAgent), |
| (r"\b(who is|who was|what is|what was|tell me about|history of)\b","fact",GeneralAgent), |
| (r"\b(calculate|compute|solve|integral|derivative|sin|cos|sqrt|factorial)\b","math",GeneralAgent), |
| |
| (r"^(hi|hey|hello|yo|sup|salaam|হ্যালো|হেই|আচ্ছা|ভালো আছ)\b","chat",FastChatAgent), |
| (r"^(thanks|thank you|ok|okay|got it|bye|kemon|bhaloi)\b","chat",FastChatAgent), |
| (r"\b(2025 book|2026 book)\b","books",SearchAgent), |
| ] |
|
|
| def route(msg,task_type=None,mode=None): |
| agents={"code":CodingAgent(),"search":SearchAgent(),"file":FileAgent(), |
| "general":GeneralAgent(),"tutor":SmartTutorAgent(),"browse":BrowseAgent()} |
| if mode=="tutor": return "tutor",SmartTutorAgent() |
| if task_type and task_type in agents: return task_type,agents[task_type] |
| lower=msg.lower().strip(); words=len(msg.split()) |
| for pat,intent,Cls in INTENT_MAP: |
| if re.search(pat,lower,re.IGNORECASE): return intent,Cls() |
| if words<6: return "chat",FastChatAgent() |
| if words>35: return "reasoning",GeneralAgent() |
| return "general",GeneralAgent() |
|
|
|
|
| |
| |
| |
| app=FastAPI(title="RUBRA API",version="8.0.0",docs_url="/docs") |
| app.add_middleware(CORSMiddleware,allow_origins=["*"],allow_credentials=True,allow_methods=["*"],allow_headers=["*"]) |
|
|
| @app.on_event("startup") |
| async def startup(): |
| t=threading.Thread(target=knowledge_loop,daemon=True); t.start() |
| log.info("✅ RUBRA v8 started — Knowledge Engine running") |
|
|
| @app.get("/") |
| async def root(): return {"name":"RUBRA","version":"8.0.0","status":"online", |
| "features":["vision","nctb_2026","uiux_pro_max","multilingual","smart_tutor","hermes_coding","exam_generator","live_knowledge"]} |
|
|
| @app.get("/health") |
| async def health(): return {"status":"ok","time":time.time()} |
|
|
| @app.post("/api/chat") |
| async def chat(req:ChatRequest): |
| sid=req.session_id or str(uuid.uuid4()); hist=mem_get(sid) |
| mem_add(sid,"user",req.message) |
| lang=detect_lang(req.message) |
| sinthia_mode = is_sinthia(req.message) |
| intent,agent=route(req.message,req.task_type,req.mode) |
| async def stream(): |
| full="" |
| try: |
| yield f"data: {json.dumps({'type':'meta','agent':agent.name,'intent':intent,'session_id':sid,'sinthia_mode': sinthia_mode,'lang':lang})}\n\n" |
| async for evt in agent.run(req.message,hist,sid,lang=lang): |
| if evt.get("type")=="token": full+=evt.get("content","") |
| yield f"data: {json.dumps(evt)}\n\n" |
| if full: mem_add(sid,"assistant",full) |
| except Exception as e: |
| log.error(f"Chat: {e}",exc_info=True) |
| yield f"data: {json.dumps({'type':'error','message':str(e)})}\n\n" |
| yield "data: [DONE]\n\n" |
| return StreamingResponse(stream(),media_type="text/event-stream", |
| headers={"Cache-Control":"no-cache","X-Accel-Buffering":"no","Connection":"keep-alive"}) |
|
|
| @app.post("/api/upload") |
| async def upload(file:UploadFile=File(...),session_id:str=Form(default=""), |
| question:str=Form(default=""),mode:str=Form(default="")): |
| sid=session_id or str(uuid.uuid4()) |
| content=await file.read() |
| fpath=UPLOAD_DIR/f"{sid}_{file.filename}" |
| fpath.write_bytes(content) |
| fname=file.filename; ext=Path(fname).suffix.lower() |
| lang=detect_lang(question); is_image=ext in IMAGE_EXTS |
| log.info(f"Upload: {fname} ({len(content):,}b) mode={mode}") |
| async def stream(): |
| full="" |
| agent_name="SmartTutorAgent" if mode=="tutor" else ("VisionAgent" if is_image else "FileAgent") |
| yield f"data: {json.dumps({'type':'meta','agent':agent_name,'intent':'file','file':fname,'session_id':sid})}\n\n" |
| try: |
| q=question or ("এই question paper টা solve করে দাও" if mode=="tutor" else f"Analyze: {fname}") |
| hist=mem_get(sid) |
| if is_image: |
| b64,mime=to_base64(fpath) |
| img_d={"data":b64,"mime":mime} |
| if mode=="tutor": |
| async for evt in SmartTutorAgent().run(q,hist,sid,lang=lang,img=img_d): |
| if evt.get("type")=="token": full+=evt.get("content","") |
| yield f"data: {json.dumps(evt)}\n\n" |
| else: |
| async for evt in VisionAgent().run(q,hist,sid,lang=lang,img=img_d): |
| if evt.get("type")=="token": full+=evt.get("content","") |
| yield f"data: {json.dumps(evt)}\n\n" |
| elif mode=="tutor" and ext==".pdf": |
| text=pdf_to_text(fpath) |
| enhanced=f"[PDF: {fname}]\n{text[:6000]}\n\nStudent question: {question or 'Solve এবং explain করো'}" |
| async for evt in SmartTutorAgent().run(enhanced,hist,sid,lang=lang): |
| if evt.get("type")=="token": full+=evt.get("content","") |
| yield f"data: {json.dumps(evt)}\n\n" |
| else: |
| async for evt in FileAgent().analyze(fpath,fname,q,sid,lang=lang): |
| if evt.get("type")=="token": full+=evt.get("content","") |
| yield f"data: {json.dumps(evt)}\n\n" |
| if full: |
| mem_add(sid,"user",f"[File: {fname}] {question}") |
| mem_add(sid,"assistant",full) |
| except Exception as e: |
| log.error(f"Upload: {e}",exc_info=True) |
| yield f"data: {json.dumps({'type':'error','message':str(e)})}\n\n" |
| yield "data: [DONE]\n\n" |
| return StreamingResponse(stream(),media_type="text/event-stream", |
| headers={"Cache-Control":"no-cache","X-Accel-Buffering":"no"}) |
|
|
| @app.post("/api/exam/generate") |
| async def gen_exam(req: ExamRequest): |
| """Legacy exam endpoint — basic text format""" |
| nctb_ctx = "" |
| for k, v in NCTB_2026.items(): |
| if req.class_.lower() in k.lower() or k.lower() in req.class_.lower(): |
| nctb_ctx = f"\n[NCTB 2026 — {k}]\n{json.dumps(v, ensure_ascii=False)[:800]}" |
| break |
| prompt = f"""{EXAM_PROMPT}{nctb_ctx} |
| Generate complete {req.type_} exam paper: |
| Subject: {req.subject} | Class: {req.class_} | Topic: {req.topic or 'Full 2026 syllabus'} |
| Questions: {req.q_count} | Language: {"Bengali (বাংলা)" if req.lang=="bn" else "English"} |
| Create a realistic NCTB 2026 curriculum exam paper with header, full questions, and answer key.""" |
| msgs = [{"role":"system","content":EXAM_PROMPT},{"role":"user","content":prompt}] |
| full = "" |
| async def collect(): |
| nonlocal full |
| async for tok in llm(msgs,"general"): full += tok |
| try: |
| await collect() |
| return JSONResponse({"ok":True,"paper":full,"subject":req.subject,"class_":req.class_}) |
| except Exception as e: |
| return JSONResponse({"ok":False,"error":str(e)},status_code=500) |
|
|
|
|
| @app.post("/api/exam/advanced") |
| async def gen_exam_advanced(body: dict): |
| """ |
| Production exam generator: |
| - 30 hard NCTB MCQ questions |
| - Optional Srijonshil questions |
| - Exam prediction |
| - Beautiful PDF output |
| """ |
| subject = body.get("subject", "বিজ্ঞান") |
| class_name = body.get("class_", "Class 10") |
| topic = body.get("topic", "") |
| lang = body.get("lang", "bn") |
| include_sq = body.get("srijonshil", False) |
| pdf_output = body.get("pdf", True) |
|
|
| try: |
| |
| questions = await generate_exam_with_ai( |
| subject=subject, |
| class_name=class_name, |
| topic=topic, |
| lang=lang, |
| include_srijonshil=include_sq, |
| include_prediction=True, |
| ) |
|
|
| if questions.get("error"): |
| return JSONResponse({"ok": False, "error": questions["error"]}, status_code=500) |
|
|
| result = { |
| "ok": True, |
| "subject": subject, |
| "class_": class_name, |
| "mcq_count": len(questions.get("mcq", [])), |
| "srijonshil_count": len(questions.get("srijonshil", [])), |
| "questions": questions, |
| } |
|
|
| |
| if pdf_output: |
| try: |
| pdf_filename = f"rubra_exam_{uuid.uuid4().hex[:8]}.pdf" |
| pdf_path = str(UPLOAD_DIR / pdf_filename) |
|
|
| time_limit = str(len(questions.get("mcq", [])) * 1) |
| total_marks = str(len(questions.get("mcq", [])) + (len(questions.get("srijonshil", [])) * 10)) |
| exam_type_label = "MCQ + সৃজনশীল" if include_sq else "বহুনির্বাচনি (MCQ)" |
|
|
| generate_exam_pdf( |
| questions_json=questions, |
| output_path=pdf_path, |
| subject=subject, |
| class_name=class_name, |
| exam_type=exam_type_label, |
| time_limit=time_limit, |
| total_marks=total_marks, |
| ) |
|
|
| |
| import base64 as b64 |
| with open(pdf_path, "rb") as f: |
| pdf_b64 = b64.b64encode(f.read()).decode() |
| os.unlink(pdf_path) |
|
|
| result["pdf_b64"] = pdf_b64 |
| result["pdf_filename"] = f"RUBRA_Exam_{subject}_{class_name}.pdf" |
|
|
| except Exception as pdf_err: |
| log.warning(f"PDF generation failed: {pdf_err}") |
| result["pdf_error"] = str(pdf_err) |
|
|
| return JSONResponse(result) |
|
|
| except Exception as e: |
| log.error(f"Advanced exam error: {e}", exc_info=True) |
| return JSONResponse({"ok": False, "error": str(e)}, status_code=500) |
|
|
|
|
| @app.get("/api/exam/predict") |
| async def exam_predict(subject: str, class_: str, topic: str = ""): |
| """Quick exam prediction — কোন topic থেকে এবার বেশি আসতে পারে""" |
| prompt = f"""বাংলাদেশ Board পরীক্ষায় ({class_}) {subject} বিষয়ে {"topic: " + topic if topic else "সম্পূর্ণ সিলেবাসে"} এবার কোন কোন অধ্যায় থেকে প্রশ্ন আসার সম্ভাবনা সবচেয়ে বেশি? |
| |
| গত ৫ বছরের pattern বিশ্লেষণ করে বলো: |
| ১. High probability topics (৫০%+ chance) |
| ২. Medium probability topics (৩০-৫০% chance) |
| ৩. যে topics থেকে এবার নতুন ধরনের প্রশ্ন আসতে পারে |
| ৪. শিক্ষার্থীদের কী পড়া উচিত |
| |
| বাংলায় উত্তর দাও। Specific chapter/topic name উল্লেখ করো।""" |
|
|
| msgs = [{"role":"system","content":TUTOR_ADVANCED_PROMPT},{"role":"user","content":prompt}] |
| full = "" |
| try: |
| async for tok in llm(msgs, "general"): full += tok |
| return JSONResponse({"ok": True, "prediction": full, "subject": subject, "class_": class_}) |
| except Exception as e: |
| return JSONResponse({"ok": False, "error": str(e)}, status_code=500) |
| |
| nctb_ctx="" |
| for k,v in NCTB_2026.items(): |
| if req.class_.lower() in k.lower() or k.lower() in req.class_.lower(): |
| nctb_ctx=f"\n[NCTB 2026 — {k}]\n{json.dumps(v,ensure_ascii=False)[:800]}" |
| break |
| prompt=f"""{EXAM_PROMPT}{nctb_ctx} |
| |
| Generate complete {req.type_} exam paper: |
| Subject: {req.subject} | Class: {req.class_} | Topic: {req.topic or 'Full 2026 syllabus'} |
| Questions: {req.q_count} | Language: {"Bengali (বাংলা)" if req.lang=="bn" else "English"} |
| |
| Create a realistic NCTB 2026 curriculum exam paper with header, full questions, and answer key.""" |
| msgs=[{"role":"system","content":EXAM_PROMPT},{"role":"user","content":prompt}] |
| full="" |
| async def collect(): |
| nonlocal full |
| async for tok in llm(msgs,"general"): full+=tok |
| try: |
| await collect() |
| return JSONResponse({"ok":True,"paper":full,"subject":req.subject,"class_":req.class_}) |
| except Exception as e: |
| return JSONResponse({"ok":False,"error":str(e)},status_code=500) |
|
|
| @app.get("/api/curriculum") |
| async def curriculum(): return {"curriculum":NCTB_2026,"year":"2026","source":"NCTB Bangladesh"} |
|
|
| @app.get("/api/live-feed") |
| async def live_feed(category:Optional[str]=None,limit:int=10): |
| return {"items":feed_get(category=category,limit=limit),"category":category} |
|
|
| @app.get("/api/trending") |
| async def trending(): |
| return {"tech":feed_get("tech",5),"ai":feed_get("ai",3),"news":feed_get("news",5),"fetched_at":time.time()} |
|
|
| @app.get("/api/sessions") |
| async def sessions(): return {"sessions":mem_sessions()} |
|
|
| @app.get("/api/sessions/{sid}") |
| async def get_session(sid:str): return {"session_id":sid,"messages":mem_get(sid,100)} |
|
|
| @app.delete("/api/sessions/{sid}") |
| async def del_session(sid:str): mem_delete(sid); return {"ok":True} |
|
|
| @app.get("/api/status") |
| async def status(): |
| stats=mem_stats() |
| return {"version":"8.0.0","stats":stats, |
| "vision_models":["Groq Llama-4-Scout (vision)","Qwen2.5-VL-72B (OpenRouter free)","GLM-4.5V (Z.AI)","OCR.space (fallback)"], |
| "features":["vision","nctb_2026","uiux_pro_max","multilingual","smart_tutor","hermes_coding","exam_generator","live_knowledge"]} |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| from fastapi import WebSocket, WebSocketDisconnect |
| import wave, asyncio, struct, io |
|
|
| GROQ_STT_URL = "https://api.groq.com/openai/v1/audio/transcriptions" |
| GEMINI_FLASH_URL = "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions" |
|
|
|
|
|
|
| |
| |
| |
| class LiveController: |
| """ |
| Gemini Live-style multimodal loop. |
| |
| WebSocket receives: |
| {type: audio_chunk|audio_end|frame|frame_clear|text|stop|interrupt|ping} |
| |
| WebSocket sends: |
| {type: idle|listening|transcript|thinking|token|tts_chunk|tts_text|done|error|interrupted|pong} |
| |
| State machine: |
| idle → listening (mic on) |
| → thinking (STT done, LLM starting) |
| → speaking (TTS streaming) |
| → done / idle |
| """ |
|
|
| def __init__(self, ws: WebSocket, sid: str): |
| self.ws = ws |
| self.sid = sid |
| self.active = True |
| self.speaking = False |
| self.listening = False |
| self.last_frame = None |
| self.audio_buf = bytearray() |
| self.lang = "en" |
| self.hist = mem_get(sid, limit=6) |
|
|
| |
| async def send(self, payload: dict): |
| try: |
| await self.ws.send_json(payload) |
| except Exception: |
| pass |
|
|
| |
| async def set_idle(self): |
| """Stop all states → send idle.""" |
| self.speaking = False |
| self.listening = False |
| await self.send({"type": "idle"}) |
|
|
| async def set_listening(self): |
| """Mic is active → send listening.""" |
| self.listening = True |
| await self.send({"type": "listening"}) |
|
|
| async def set_thinking(self): |
| """STT done, LLM starting → send thinking.""" |
| self.listening = False |
| await self.send({"type": "thinking"}) |
|
|
| |
| async def interrupt(self): |
| if self.speaking: |
| self.speaking = False |
| await self.send({"type": "interrupted"}) |
| |
| await self.set_listening() |
|
|
| |
| async def transcribe(self, audio_bytes: bytes) -> str: |
| if len(audio_bytes) < 300: |
| return "" |
| try: |
| import aiohttp |
|
|
| wav_buf = io.BytesIO() |
| with wave.open(wav_buf, 'wb') as wf: |
| wf.setnchannels(1) |
| wf.setsampwidth(2) |
| wf.setframerate(16000) |
| wf.writeframes(audio_bytes) |
| wav_buf.seek(0) |
|
|
| form = aiohttp.FormData() |
| form.add_field("file", wav_buf, filename="audio.wav", content_type="audio/wav") |
| form.add_field("model", "whisper-large-v3-turbo") |
| form.add_field("response_format", "text") |
| form.add_field("language", "bn" if self.lang == "bn" else "en") |
|
|
| headers = {"Authorization": f"Bearer {GROQ_KEY}"} |
| timeout = aiohttp.ClientTimeout(total=12) |
| async with aiohttp.ClientSession(timeout=timeout) as s: |
| async with s.post(GROQ_STT_URL, headers=headers, data=form) as r: |
| if r.status == 200: |
| return (await r.text()).strip() |
| log.warning(f"STT {r.status}: {await r.text()}") |
| except Exception as e: |
| log.warning(f"STT error: {e}") |
| return "" |
|
|
| |
| async def llm_stream(self, text: str): |
| """Stream response from Gemini Flash with optional vision frame.""" |
| sys_p = ( |
| f"{RUBRA_CORE}\n\n" |
| "[LIVE VOICE MODE] You are in real-time voice conversation. Rules:\n" |
| "• Responses must be SHORT (1-3 sentences max unless asked)\n" |
| "• Natural spoken language — no markdown, no bullet points\n" |
| "• If you see a screen/camera frame, describe what's relevant\n" |
| "• Language: match user's language exactly\n" |
| ) |
| if self.lang == "bn": |
| sys_p += "\n⚡ Reply in Bengali (বাংলা) — short, spoken style." |
|
|
| user_content = [] |
| if self.last_frame: |
| user_content.append({ |
| "type": "image_url", |
| "image_url": { |
| "url": f"data:image/jpeg;base64,{self.last_frame}", |
| "detail": "low" |
| } |
| }) |
| user_content.append({"type": "text", "text": text}) |
|
|
| messages = [{"role": "system", "content": sys_p}] |
| for h in self.hist[-4:]: |
| if h.get("role") in ("user", "assistant"): |
| messages.append({"role": h["role"], "content": h["content"]}) |
| messages.append({"role": "user", "content": user_content}) |
|
|
| endpoints = [ |
| (GEMINI_FLASH_URL, GEMINI_KEY, "gemini-2.5-flash-preview-05-20"), |
| (GROQ_URL, GROQ_KEY, "meta-llama/llama-4-scout-17b-16e-instruct"), |
| (ZAI_CHAT, ZAI_KEY, "glm-4.7-flash"), |
| ] |
|
|
| for url, key, model in endpoints: |
| try: |
| import aiohttp |
| headers = {"Authorization": f"Bearer {key}", "Content-Type": "application/json"} |
| req_url = f"{url}?key={key}" if "generativelanguage" in url else url |
| payload = { |
| "model": model, "messages": messages, |
| "stream": True, "max_tokens": 300, "temperature": 0.7, |
| } |
| timeout = aiohttp.ClientTimeout(total=20, connect=5) |
| async with aiohttp.ClientSession(timeout=timeout) as s: |
| async with s.post(req_url, headers=headers, json=payload) as resp: |
| if resp.status not in (200, 201): |
| raise Exception(f"HTTP {resp.status}") |
| async for line in resp.content: |
| if not self.active or not self.speaking: |
| return |
| line = line.decode().strip() |
| if not line or line == "data: [DONE]": |
| continue |
| if line.startswith("data: "): |
| line = line[6:] |
| try: |
| tok = json.loads(line)["choices"][0] \ |
| .get("delta", {}).get("content", "") |
| if tok: |
| yield tok |
| except Exception: |
| pass |
| return |
| except Exception as e: |
| log.warning(f"Live LLM {model}: {e}") |
| continue |
|
|
| |
| async def tts_stream(self, text: str): |
| """Convert text to audio and stream chunks to client.""" |
| if not text.strip(): |
| return |
|
|
| clean = re.sub(r'[*#`\[\]_~>]', '', text) |
| clean = re.sub(r'https?://\S+', '', clean) |
| if self.lang == "bn": |
| clean = re.sub(r'(\d+)', r' \1 ', clean) |
| clean = re.sub(r'[(){}\[\]<>|\\/@#$%^&*+=]', ' ', clean) |
| clean = re.sub(r'\s+', ' ', clean).strip() |
| if not clean: |
| return |
|
|
| voice_map = { |
| "en": ("en-US-AriaNeural", "-8%", "+10Hz"), |
| "bn": ("bn-BD-NabanitaNeural", "-5%", "+5Hz"), |
| "bn_roman": ("en-US-AriaNeural", "-8%", "+10Hz"), |
| } |
| voice, rate, pitch = voice_map.get(self.lang, voice_map["en"]) |
|
|
| try: |
| import edge_tts |
| comm = edge_tts.Communicate(clean, voice, rate=rate, pitch=pitch) |
| async for chunk in comm.stream(): |
| if not self.active or not self.speaking: |
| return |
| if chunk["type"] == "audio": |
| b64 = base64.b64encode(chunk["data"]).decode() |
| await self.send({"type": "tts_chunk", "audio_b64": b64}) |
| except Exception as e: |
| log.warning(f"TTS stream error: {e}") |
| await self.send({"type": "tts_text", "text": clean}) |
|
|
| |
| async def respond(self, text: str): |
| """Full pipeline: thinking → LLM stream → TTS stream → idle.""" |
| if not text.strip(): |
| return |
|
|
| self.speaking = True |
| await self.set_thinking() |
|
|
| full_response = "" |
| sentence_buf = "" |
| sentence_ends = {'.', '!', '?', '।'} |
|
|
| async for tok in self.llm_stream(text): |
| if not self.active or not self.speaking: |
| break |
|
|
| full_response += tok |
| sentence_buf += tok |
|
|
| await self.send({"type": "token", "content": tok}) |
|
|
| if any(c in sentence_buf for c in sentence_ends) and len(sentence_buf) > 12: |
| asyncio.create_task(self.tts_stream(sentence_buf)) |
| sentence_buf = "" |
|
|
| if sentence_buf.strip() and self.speaking: |
| asyncio.create_task(self.tts_stream(sentence_buf)) |
|
|
| if full_response: |
| mem_add(self.sid, "user", text) |
| mem_add(self.sid, "assistant", full_response) |
| self.hist = mem_get(self.sid, limit=6) |
|
|
| self.speaking = False |
| await self.send({"type": "done"}) |
| await self.set_idle() |
|
|
| |
| async def run(self): |
| """Process incoming WebSocket messages.""" |
| |
| await self.set_idle() |
|
|
| try: |
| while self.active: |
| try: |
| raw = await asyncio.wait_for( |
| self.ws.receive_json(), timeout=90 |
| ) |
| except asyncio.TimeoutError: |
| await self.send({"type": "ping"}) |
| continue |
|
|
| msg_type = raw.get("type", "") |
|
|
| |
| if msg_type == "audio_chunk": |
| chunk = base64.b64decode(raw.get("data", "")) |
| self.audio_buf.extend(chunk) |
|
|
| |
| if not self.listening and not self.speaking: |
| await self.set_listening() |
|
|
| |
| if len(self.audio_buf) > 8000: |
| audio = bytes(self.audio_buf) |
| self.audio_buf = bytearray() |
| if self.speaking: |
| await self.interrupt() |
| |
| transcript = await self.transcribe(audio) |
| if transcript and len(transcript) > 2: |
| self.lang = detect_lang(transcript) |
| await self.send({"type": "transcript", "text": transcript}) |
| await self.respond(transcript) |
| else: |
| |
| await self.set_idle() |
|
|
| |
| elif msg_type == "audio_end": |
| if self.speaking: |
| await self.interrupt() |
| if self.audio_buf: |
| audio_data = bytes(self.audio_buf) |
| self.audio_buf = bytearray() |
| transcript = await self.transcribe(audio_data) |
| if transcript: |
| self.lang = detect_lang(transcript) |
| await self.send({"type": "transcript", "text": transcript}) |
| await self.respond(transcript) |
| else: |
| await self.set_idle() |
| else: |
| |
| await self.set_idle() |
|
|
| |
| elif msg_type == "text": |
| if self.speaking: |
| await self.interrupt() |
| text = raw.get("text", "").strip() |
| if text: |
| self.lang = detect_lang(text) |
| await self.respond(text) |
| else: |
| await self.set_idle() |
|
|
| |
| elif msg_type == "frame": |
| frame = raw.get("data", "") |
| if "base64," in frame: |
| frame = frame.split("base64,")[1] |
| self.last_frame = frame[:180000] |
|
|
| elif msg_type == "frame_clear": |
| self.last_frame = None |
|
|
| |
| elif msg_type == "interrupt": |
| await self.interrupt() |
|
|
| |
| elif msg_type == "ping": |
| await self.send({"type": "pong"}) |
|
|
| |
| elif msg_type == "stop": |
| self.active = False |
| await self.set_idle() |
| break |
|
|
| except WebSocketDisconnect: |
| pass |
| except Exception as e: |
| log.error(f"LiveController: {e}") |
| await self.send({"type": "error", "message": str(e)[:100]}) |
| finally: |
| self.active = False |
|
|
|
|
| |
| @app.websocket("/ws/live/{session_id}") |
| async def live_endpoint(websocket: WebSocket, session_id: str): |
| await websocket.accept() |
| log.info(f"🔴 Live session: {session_id}") |
|
|
| controller = LiveController(websocket, session_id) |
| try: |
| await websocket.send_json({ |
| "type": "ready", |
| "message": "Rubra Live connected", |
| "session": session_id, |
| }) |
| await controller.run() |
| except WebSocketDisconnect: |
| pass |
| finally: |
| controller.active = False |
| log.info(f"⭕ Live session ended: {session_id}") |
|
|
| |
| import asyncio |
| _live_sessions = {} |
| _live_frames = {} |
|
|
| @app.post("/api/live/frame") |
| async def live_frame(body: dict): |
| sid = body.get("session_id", "default") |
| frame = body.get("frame", "") |
| if "base64," in frame: |
| frame = frame.split("base64,")[1] |
| _live_frames[sid] = frame[:180000] |
| return JSONResponse({"ok": True}) |
|
|
| @app.post("/api/live/send") |
| async def live_send(body: dict): |
| sid = body.get("session_id", "default") |
| text = (body.get("text") or "").strip() |
| if not text: |
| return JSONResponse({"error": "no text"}, status_code=400) |
| if sid not in _live_sessions: |
| _live_sessions[sid] = asyncio.Queue() |
| detected = detect_lang(text) |
| await _live_sessions[sid].put({"text": text, "lang": detected}) |
| return JSONResponse({"ok": True, "detected_lang": detected, "received": text}) |
|
|
| @app.get("/api/live/stream/{session_id}") |
| async def live_stream(session_id: str): |
| if session_id not in _live_sessions: |
| _live_sessions[session_id] = asyncio.Queue() |
|
|
| async def generate(): |
| hist = mem_get(session_id, limit=6) |
| yield f"data: {json.dumps({'type':'ready'})}\n\n" |
|
|
| while True: |
| try: |
| item = await asyncio.wait_for( |
| _live_sessions[session_id].get(), timeout=30 |
| ) |
| except asyncio.TimeoutError: |
| yield f"data: {json.dumps({'type':'ping'})}\n\n" |
| continue |
|
|
| text = item["text"] |
| detected_lang = detect_lang(text) |
| yield f"data: {json.dumps({'type':'thinking'})}\n\n" |
| sys_p = ( |
| f"{RUBRA_CORE}\n\n" |
| "[LIVE VOICE MODE RULES]\n" |
| "• SHORT answers — 1-3 sentences for simple questions\n" |
| "• No markdown, no bullet points — natural spoken language only\n" |
| "• Even if user types broken/unclear text, understand intent and answer correctly\n" |
| "• If user text looks like speech-to-text errors, fix and understand it\n" |
| "• Match user language exactly\n" |
| ) |
| if detected_lang == "bn": |
| sys_p += "• বাংলায় উত্তর দাও। সংক্ষিপ্ত, স্বাভাবিক কথার ভাষায়।\n" |
| messages = [{"role": "system", "content": sys_p}] |
| for h in hist[-4:]: |
| if h.get("role") in ("user", "assistant"): |
| messages.append({"role": h["role"], "content": h["content"]}) |
| |
| |
| frame = _live_frames.get(session_id) |
| if frame: |
| messages.append({"role": "user", "content": [ |
| {"type": "image_url", "image_url": { |
| "url": f"data:image/jpeg;base64,{frame}", |
| "detail": "low" |
| }}, |
| {"type": "text", "text": text} |
| ]}) |
| else: |
| messages.append({"role": "user", "content": text}) |
|
|
| fast_endpoints = [ |
| (GROQ_URL, GROQ_KEY, "llama-3.3-70b-versaetil"), |
| (CEREBRAS_URL, CEREBRAS_KEY, "llama-3.3-70b"), |
| (CEREBRAS_URL, CEREBRAS_KEY, "llama-4-scout-17b-16e-instruct"), |
| (GROQ_URL, GROQ_KEY, "meta-llama/llama-4-scout-17b-16e-instruct"), |
| (ZAI_CHAT, ZAI_KEY, "glm-4.7-flash"), |
| ] |
|
|
| |
|
|
| full = "" |
| for url, key, model in fast_endpoints: |
| try: |
| headers = {"Authorization": f"Bearer {key}", "Content-Type": "application/json"} |
| req_url = f"{url}?key={key}" if "generativelanguage" in url else url |
| payload = { |
| "model": model, "messages": messages, |
| "stream": True, "max_tokens": 150, "temperature": 0.7 |
| } |
| timeout = aiohttp.ClientTimeout(total=15, connect=4) |
| async with aiohttp.ClientSession(timeout=timeout) as s: |
| async with s.post(req_url, headers=headers, json=payload) as resp: |
| if resp.status not in (200, 201): |
| raise Exception(f"HTTP {resp.status}") |
| async for line in resp.content: |
| line = line.decode().strip() |
| if not line or line == "data: [DONE]": continue |
| if line.startswith("data: "): line = line[6:] |
| try: |
| tok = json.loads(line)["choices"][0].get("delta", {}).get("content", "") |
| if tok: |
| full += tok |
| yield f"data: {json.dumps({'type':'token','content':tok})}\n\n" |
| except: pass |
| if full: break |
| except Exception as e: |
| |
| continue |
|
|
| if full: |
| mem_add(session_id, "user", text) |
| mem_add(session_id, "assistant", full) |
| hist = mem_get(session_id, limit=6) |
| try: |
| import edge_tts |
|
|
| def clean_for_tts(text: str, lang: str) -> str: |
| text = re.sub(r'[*#`\[\]_~>]', '', text) |
| text = re.sub(r'https?://\S+', '', text) |
| if lang == "bn": |
| text = re.sub(r'(\d+)', r' \1 ', text) |
| text = re.sub(r'[(){}\[\]<>|\\/@#$%^&*+=]', ' ', text) |
| text = re.sub(r'\s+', ' ', text).strip() |
| return text[:500] |
|
|
| tts_text = clean_for_tts(full, detected_lang) |
| if not tts_text: |
| raise ValueError("Empty TTS text") |
|
|
| if detected_lang == "bn": |
| voice = "bn-BD-NabanitaNeural" |
| rate = "-10%" |
| pitch = "+0Hz" |
| elif detected_lang == "bn_roman": |
| voice = "en-US-AriaNeural" |
| rate = "-8%" |
| pitch = "+10Hz" |
| else: |
| voice = "en-US-AriaNeural" |
| rate = "-8%" |
| pitch = "+10Hz" |
|
|
| tmp = HERE / f"_live_{uuid.uuid4().hex[:6]}.mp3" |
| communicate = edge_tts.Communicate(tts_text, voice, rate=rate, pitch=pitch) |
| await communicate.save(str(tmp)) |
|
|
| if tmp.exists() and tmp.stat().st_size > 100: |
| b64 = base64.b64encode(tmp.read_bytes()).decode() |
| tmp.unlink(missing_ok=True) |
| yield f"data: {json.dumps({'type':'tts_chunk','audio_b64':b64})}\n\n" |
| else: |
| tmp.unlink(missing_ok=True) |
| raise ValueError("TTS file empty") |
|
|
| except Exception as tts_err: |
| log.warning(f"TTS failed: {tts_err}") |
| yield f"data: {json.dumps({'type':'tts_text','text':full})}\n\n" |
|
|
| |
|
|
| return StreamingResponse( |
| generate(), |
| media_type="text/event-stream", |
| headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"} |
| ) |
| @app.get("/api/tools/weather") |
| async def w(city:str="Dhaka"): return tool_weather(city) or {"error":"Unavailable"} |
|
|
| @app.get("/api/tools/crypto") |
| async def c(coins:str="bitcoin,ethereum"): return tool_crypto(coins) or {"error":"Unavailable"} |
|
|
| @app.get("/api/tools/currency") |
| async def fx(base:str="USD"): return tool_currency(base) or {"error":"Unavailable"} |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| RUBRA_VOICE_PROFILE = { |
| "en": {"voice": "en-US-AriaNeural", "rate": "-8%", "pitch": "+10Hz", "style": "assistant"}, |
| "bn": {"voice": "bn-BD-NabanitaNeural", "rate": "-5%", "pitch": "+5Hz", "style": ""}, |
| "bn_roman": {"voice": "en-US-AriaNeural", "rate": "-8%", "pitch": "+10Hz", "style": "assistant"}, |
| } |
|
|
| @app.post("/api/tts") |
| async def tts_endpoint(body: dict): |
| text = (body.get("text") or "").strip()[:700] |
| lang = body.get("lang", "en") |
| if not text: |
| return JSONResponse({"error": "No text provided"}, status_code=400) |
|
|
| clean = re.sub(r'[*#`\[\]_~>]', '', text) |
| clean = re.sub(r'https?://\S+', '', clean) |
| if lang == "bn": |
| clean = re.sub(r'(\d+)', r' \1 ', clean) |
| clean = re.sub(r'[(){}\[\]<>|\\/@#$%^&*+=]', ' ', clean) |
| clean = re.sub(r'\s+', ' ', clean).strip() |
| if not clean: |
| return JSONResponse({"error": "Empty text after cleaning"}, status_code=400) |
|
|
| profile = RUBRA_VOICE_PROFILE.get(lang, RUBRA_VOICE_PROFILE["en"]) |
| voice = profile["voice"] |
| rate = profile["rate"] |
| pitch = profile["pitch"] |
|
|
| if lang == "bn": |
| rate = "-10%" |
| pitch = "+0Hz" |
|
|
| try: |
| import edge_tts |
|
|
| communicate = edge_tts.Communicate(clean, voice, rate=rate, pitch=pitch) |
|
|
| tmp_path = HERE / f"_tts_{uuid.uuid4().hex[:8]}.mp3" |
| await communicate.save(str(tmp_path)) |
|
|
| if not tmp_path.exists() or tmp_path.stat().st_size < 100: |
| tmp_path.unlink(missing_ok=True) |
| return JSONResponse({"error": "TTS generation failed"}, status_code=500) |
|
|
| audio_bytes = tmp_path.read_bytes() |
| tmp_path.unlink(missing_ok=True) |
|
|
| b64 = base64.b64encode(audio_bytes).decode() |
| return JSONResponse({ |
| "audio_b64": b64, |
| "voice": voice, |
| "lang": lang, |
| "duration_est": len(clean) / 15, |
| }) |
|
|
| except ImportError: |
| return JSONResponse( |
| {"error": "edge-tts not installed. Run: pip install edge-tts"}, |
| status_code=500 |
| ) |
| except Exception as e: |
| log.warning(f"TTS error: {e}") |
| return JSONResponse({"error": str(e)[:200]}, status_code=500) |
|
|
|
|
| @app.get("/api/tts/voices") |
| async def tts_voices(): |
| """List available Edge TTS female voices.""" |
| try: |
| import edge_tts |
| all_voices = await edge_tts.list_voices() |
| female = [ |
| {"name": v["ShortName"], "lang": v["Locale"], "gender": v["Gender"]} |
| for v in all_voices if v.get("Gender") == "Female" |
| ] |
| return {"voices": female, "rubra_primary": "en-US-AriaNeural"} |
| except ImportError: |
| return {"error": "pip install edge-tts"} |
| |
|
|
| @app.get("/api/skills") |
| async def list_skills_route(): |
| skills = skill_list() |
| return {"skills":[{"name":s,"lines":len((SKILLS_DIR/f"{s}.py").read_text().splitlines())} for s in skills],"count":len(skills)} |
|
|
| @app.post("/api/skills/generate") |
| async def gen_skill(body: dict): |
| name=re.sub(r"[^a-z0-9_]","_",body.get("name","custom").lower()); task=body.get("task","") |
| if not task: return JSONResponse({"error":"task required"},status_code=400) |
| code=await skill_generate_and_save(name,task) |
| return {"name":name,"code":code,"saved":True} |
|
|
| @app.get("/api/session/{sid}/memory") |
| async def get_memory(sid:str): return session_load(sid) |
|
|
| @app.get("/api/session/{sid}/thoughts") |
| async def get_thoughts(sid:str): |
| data=session_load(sid); return {"thoughts":data.get("thought_chain",[])[-10:]} |
|
|
| |
| if __name__=="__main__": |
| import uvicorn |
| PORT=int(os.getenv("PORT",7860)) |
| print() |
| print("="*60) |
| print(" RUBRA v8 — Ultimate Agentic Intelligence") |
| print(" ✓ Vision: Groq Vision + Qwen2.5-VL + OCR.space") |
| print(" ✓ NCTB 2026 Full Curriculum Built-in") |
| print(" ✓ UI/UX Pro Max Coding (67 styles, 161 rules)") |
| print(" ✓ Hermes Ultra Coding Engine") |
| print(" ✓ Multilingual: Bangla/Banglish/English") |
| print(" ✓ Smart Tutor (JSC/SSC/HSC/Primary)") |
| print(" ✓ Live Knowledge (auto-updates every 25min)") |
| print(f" Running on port {PORT}") |
| print("="*60) |
| print() |
| uvicorn.run(app,host="0.0.0.0",port=PORT,reload=False) |
|
|