| """ |
| RugCharts Model Registry & Quality Standards |
| ============================================= |
| Smart model routing across free providers. Quality review pipeline. |
| All AI tasks go through this module. All output meets human standards. |
| |
| Free Models Available (OpenRouter): |
| NVIDIA Nemotron 3 Super 120B β research, analysis, long context (1M) |
| Google Gemma 4 26B β writing, prose, natural language |
| NVIDIA Nemotron Nano 30B β reasoning, classification |
| Qwen3 Coder 480B β code generation, tool use |
| Moonshot Kimi K2.6 β fast writing, summaries |
| Z.ai GLM 4.5 Air β general purpose, fast |
| OpenAI gpt-oss-120b β heavy reasoning, agentic tasks |
| OpenAI gpt-oss-20b β lightweight, fast inference |
| Liquid LFM 2.5 1.2B β edge, tiny tasks, classification |
| |
| Other Free Providers: |
| Groq (Llama 3.1 8B, Llama 3.3 70B) β 14,400 RPD free |
| Mistral (via OpenRouter free tier) |
| DeepSeek Flash V4 β $0.14/M (near-free with prefix caching) |
| |
| Quality Standards: |
| NO: "delve", "tapestry", "landscape", "robust", "moreover", "furthermore", |
| "in conclusion", "it is worth noting", "underscores", "showcasing", |
| "a testament to", "in the realm of", "paradigm shift" |
| YES: direct, specific, human voice, numbers, names, concrete details |
| ALWAYS: review step before publishing |
| """ |
|
|
| import json |
| import logging |
| import os |
| import time |
| from collections import defaultdict |
|
|
| import httpx |
|
|
| logger = logging.getLogger("model_registry") |
|
|
| OPENROUTER_KEY = os.getenv("OPENROUTER_API_KEY", "") |
| GROQ_KEY = os.getenv("GROQ_API_KEY", "") |
| MISTRAL_KEY = os.getenv("MISTRAL_API_KEY", "") |
| OR_URL = "https://openrouter.ai/api/v1/chat/completions" |
| GROQ_URL = "https://api.groq.com/openai/v1/chat/completions" |
| MISTRAL_URL = "https://api.mistral.ai/v1/chat/completions" |
|
|
| |
|
|
| MODELS = { |
| |
| "research": { |
| "primary": { |
| "id": "nvidia/nemotron-3-super-120b-a12b:free", |
| "provider": "openrouter", |
| "context": 1000000, |
| "cost_per_1k": 0, |
| "rpm": 20, |
| "strengths": ["long_context", "analysis", "data_synthesis", "multi_document"], |
| }, |
| "fallback": { |
| "id": "nvidia/nemotron-3-nano-30b-a3b:free", |
| "provider": "openrouter", |
| "context": 256000, |
| "cost_per_1k": 0, |
| "rpm": 20, |
| "strengths": ["reasoning", "analysis", "structured_output"], |
| }, |
| }, |
| |
| "writing": { |
| "primary": { |
| "id": "nvidia/nemotron-3-super-120b-a12b:free", |
| "provider": "openrouter", |
| "context": 1000000, |
| "cost_per_1k": 0, |
| "rpm": 20, |
| "strengths": ["natural_prose", "long_context", "creative"], |
| }, |
| "fallback": { |
| "id": "nvidia/nemotron-3-nano-30b-a3b:free", |
| "provider": "openrouter", |
| "context": 256000, |
| "cost_per_1k": 0, |
| "rpm": 20, |
| "strengths": ["reasoning", "writing", "structured"], |
| }, |
| }, |
| |
| "coding": { |
| "primary": { |
| "id": "nvidia/nemotron-3-super-120b-a12b:free", |
| "provider": "openrouter", |
| "context": 1000000, |
| "cost_per_1k": 0, |
| "rpm": 20, |
| "strengths": ["code_gen", "agentic", "long_context"], |
| }, |
| "fallback": { |
| "id": "nvidia/nemotron-3-nano-30b-a3b:free", |
| "provider": "openrouter", |
| "context": 256000, |
| "cost_per_1k": 0, |
| "rpm": 20, |
| "strengths": ["reasoning", "code", "structured_output"], |
| }, |
| }, |
| |
| "review": { |
| "primary": { |
| "id": "nvidia/nemotron-3-nano-30b-a3b:free", |
| "provider": "openrouter", |
| "context": 256000, |
| "cost_per_1k": 0, |
| "rpm": 20, |
| "strengths": ["proofreading", "error_detection", "consistency"], |
| }, |
| "fallback": { |
| "id": "z-ai/glm-4.5-air:free", |
| "provider": "openrouter", |
| "context": 131000, |
| "cost_per_1k": 0, |
| "rpm": 30, |
| "strengths": ["speed", "classification", "simple_tasks"], |
| }, |
| }, |
| |
| "fast": { |
| "primary": { |
| "id": "z-ai/glm-4.5-air:free", |
| "provider": "openrouter", |
| "context": 131000, |
| "cost_per_1k": 0, |
| "rpm": 30, |
| "strengths": ["speed", "classification", "simple_tasks"], |
| }, |
| "fallback": { |
| "id": "nvidia/nemotron-3-nano-30b-a3b:free", |
| "provider": "openrouter", |
| "context": 256000, |
| "cost_per_1k": 0, |
| "rpm": 20, |
| "strengths": ["reasoning", "general", "reliable"], |
| }, |
| "groq": { |
| "id": "llama-3.1-8b-instant", |
| "provider": "groq", |
| "context": 128000, |
| "cost_per_1k": 0, |
| "rpm": 30, |
| "strengths": ["speed", "sub_100ms_ttft", "high_throughput"], |
| }, |
| }, |
| |
| "writing_groq": { |
| "primary": { |
| "id": "llama-3.3-70b-versatile", |
| "provider": "groq", |
| "context": 128000, |
| "cost_per_1k": 0, |
| "rpm": 30, |
| "strengths": ["writing", "speed", "quality_prose"], |
| }, |
| "fallback": { |
| "id": "llama-3.1-8b-instant", |
| "provider": "groq", |
| "context": 128000, |
| "cost_per_1k": 0, |
| "rpm": 30, |
| "strengths": ["speed", "throughput", "reliable"], |
| }, |
| }, |
| |
| "mistral_write": { |
| "primary": { |
| "id": "mistral-small-latest", |
| "provider": "mistral", |
| "context": 262144, |
| "cost_per_1k": 0, |
| "rpm": 30, |
| "strengths": ["writing", "balanced", "multilingual"], |
| }, |
| "fallback": { |
| "id": "ministral-8b-latest", |
| "provider": "mistral", |
| "context": 262144, |
| "cost_per_1k": 0, |
| "rpm": 30, |
| "strengths": ["speed", "efficient", "good_prose"], |
| }, |
| }, |
| "mistral_code": { |
| "primary": { |
| "id": "codestral-latest", |
| "provider": "mistral", |
| "context": 256000, |
| "cost_per_1k": 0, |
| "rpm": 30, |
| "strengths": ["code_gen", "fill_in_middle", "agentic"], |
| }, |
| }, |
| "mistral_fast": { |
| "primary": { |
| "id": "ministral-3b-latest", |
| "provider": "mistral", |
| "context": 131072, |
| "cost_per_1k": 0, |
| "rpm": 30, |
| "strengths": ["speed", "tiny", "classification"], |
| }, |
| "fallback": { |
| "id": "mistral-tiny-latest", |
| "provider": "mistral", |
| "context": 131072, |
| "cost_per_1k": 0, |
| "rpm": 30, |
| "strengths": ["speed", "simple_tasks", "high_throughput"], |
| }, |
| }, |
| } |
|
|
| |
| |
|
|
| AI_ROLES = { |
| "advisor": { |
| "name": "Platform Advisor", |
| "emoji": "π‘οΈ", |
| "description": "Monitors system health, rate limits, anomalies. Proactive alerts.", |
| "model": "nvidia/nemotron-3-nano-30b-a3b:free", |
| "provider": "openrouter", |
| "budget": {"per_hour": 10, "per_day": 50}, |
| "temperature": 0.2, |
| "data_classifier": { |
| "name": "Data Classifier", |
| "emoji": "π·οΈ", |
| "description": "Categorizes articles, detects sentiment, tags content. High throughput on Groq.", |
| "model": "llama-3.1-8b-instant", |
| "provider": "groq", |
| "fallback": "ministral-3b-latest", |
| "fallback_provider": "mistral", |
| "budget": {"per_minute": 25, "per_day": 3000}, |
| "temperature": 0.1, |
| }, |
| "social_writer": { |
| "name": "Social Media Writer", |
| "emoji": "π", |
| "description": "X/Twitter posts, Telegram messages. Runs on Groq, high throughput.", |
| "model": "llama-3.1-8b-instant", |
| "provider": "groq", |
| "fallback": "mistral-small-latest", |
| "fallback_provider": "mistral", |
| "budget": {"per_task": 2, "per_day": 50}, |
| "temperature": 0.8, |
| }, |
| "cron_worker": { |
| "name": "Cron Worker", |
| "emoji": "β°", |
| "description": "Scheduled tasks. Primary on Mistral (unlimited), fallback Groq.", |
| "model": "ministral-3b-latest", |
| "provider": "mistral", |
| "fallback": "llama-3.1-8b-instant", |
| "fallback_provider": "groq", |
| "budget": {"per_task": 5, "per_day": 200}, |
| "temperature": 0.5, |
| }, |
| "content_writer": { |
| "name": "Content Writer", |
| "emoji": "βοΈ", |
| "description": "Quality prose. Mistral primary, Groq for volume.", |
| "model": "mistral-small-latest", |
| "provider": "mistral", |
| "fallback": "llama-3.3-70b-versatile", |
| "fallback_provider": "groq", |
| "budget": {"per_task": 3, "per_day": 30}, |
| "temperature": 0.7, |
| }, |
| "advisor": { |
| "name": "Platform Advisor", |
| "emoji": "π‘οΈ", |
| "description": "System health. Uses Groq (never touches OpenRouter research quota).", |
| "model": "llama-3.3-70b-versatile", |
| "provider": "groq", |
| "fallback": "mistral-small-latest", |
| "fallback_provider": "mistral", |
| "budget": {"per_hour": 10, "per_day": 100}, |
| "temperature": 0.2, |
| }, |
| }, |
| "rag_embedder": { |
| "name": "RAG Embedder", |
| "emoji": "π§ ", |
| "description": "Vector embeddings. Uses NVIDIA NIM directly (NOT OpenRouter) to avoid quota conflict. Batch + cache.", |
| "model": "nvidia/nemo-embed-12b", |
| "provider": "nvidia_nim", |
| "budget": {"per_day": 50000, "batch_size": 100}, |
| "temperature": 0.0, |
| "strategy": "BATCH: embed 100 docs per call. CACHE: never re-embed. LOCAL: consider sentence-transformers for hot path.", |
| }, |
| "security_auditor": { |
| "name": "Security Auditor", |
| "emoji": "π", |
| "description": "Scans code/configs for vulnerabilities, exposed keys, unsafe patterns.", |
| "model": "nvidia/nemotron-3-super-120b-a12b:free", |
| "provider": "openrouter", |
| "budget": {"per_task": 5, "per_day": 10}, |
| "temperature": 0.1, |
| }, |
| "data_classifier": { |
| "name": "Data Classifier", |
| "emoji": "π·οΈ", |
| "description": "Categorizes articles, detects sentiment, tags content. High throughput.", |
| "model": "ministral-3b-latest", |
| "provider": "mistral", |
| "fallback": "z-ai/glm-4.5-air:free", |
| "fallback_provider": "openrouter", |
| "budget": {"per_minute": 20, "per_day": 500}, |
| "temperature": 0.1, |
| }, |
| "social_writer": { |
| "name": "Social Media Writer", |
| "emoji": "π", |
| "description": "X/Twitter posts, Telegram messages. Punchy, engaging, native to platform.", |
| "model": "mistral-small-latest", |
| "provider": "mistral", |
| "fallback": "llama-3.1-8b-instant", |
| "fallback_provider": "groq", |
| "budget": {"per_task": 2, "per_day": 20}, |
| "temperature": 0.8, |
| }, |
| "fact_checker": { |
| "name": "Fact Checker", |
| "emoji": "β
", |
| "description": "Verifies claims against known data. Cross-references sources.", |
| "model": "nvidia/nemotron-3-super-120b-a12b:free", |
| "provider": "openrouter", |
| "budget": {"per_task": 3, "per_day": 15}, |
| "temperature": 0.1, |
| }, |
| } |
|
|
| |
| |
|
|
| PROVIDER_LIMITS = { |
| "openrouter": { |
| "name": "OpenRouter", |
| "rpm": 20, |
| "rpd_free_no_credits": 50, |
| "rpd_free_with_credits": 1000, |
| "current_tier": "paid", |
| "free_model_suffix": ":free", |
| "check_endpoint": "https://openrouter.ai/api/v1/key", |
| }, |
| "groq": { |
| "name": "Groq", |
| "rpm": 30, |
| "rpd": 14400, |
| "tpm": 6000, |
| "current_tier": "free", |
| "models": ["llama-3.3-70b-versatile", "llama-3.1-8b-instant"], |
| }, |
| "mistral": { |
| "name": "Mistral", |
| "rps": 1, |
| "tpm": 500000, |
| "tpm_budget": 1000000000, |
| "current_tier": "free", |
| }, |
| "nvidia_nim": { |
| "name": "NVIDIA NIM", |
| "rpm": 100, |
| "rpd": 5000, |
| "current_tier": "free", |
| "base_url": "https://integrate.api.nvidia.com/v1", |
| "key_models": [ |
| "nvidia/nemotron-3-super-120b-a12b", |
| "nvidia/nemotron-3-nano-30b-a3b", |
| "nvidia/nv-embedqa-e5-v5", |
| "nvidia/llama-3.3-nemotron-super-49b-v1", |
| "nvidia/nemotron-4-340b-instruct", |
| "meta/llama-3.3-70b-instruct", |
| "deepseek-ai/deepseek-v4-flash", |
| "google/gemma-4-31b-it", |
| "mistralai/mistral-large-3-675b-instruct", |
| "qwen/qwen3-coder-480b-a35b-instruct", |
| "baai/bge-m3", |
| "snowflake/arctic-embed-line", |
| ], |
| }, |
| } |
|
|
| |
| |
|
|
|
|
| class RateLimitTracker: |
| """Tracks API usage across all providers. Respects hard limits. |
| |
| Budget allocation (of 1,000 OpenRouter + 14,400 Groq + Mistral): |
| - Daily Intel report: 3-5 calls/day (research + write + review) |
| - CT Rundown: 1-2 calls/day (summarize) |
| - Content review: 5-10 calls/day (quality checks) |
| - Background tasks: 10-20 calls/day (classification, enrichment) |
| - Peak headroom: ~950 calls/day remaining for bursts |
| """ |
|
|
| def __init__(self): |
| self._minute: dict[str, int] = defaultdict(int) |
| self._hour: dict[str, int] = defaultdict(int) |
| self._day: dict[str, int] = defaultdict(int) |
| self._minute_start = time.time() |
| self._hour_start = time.time() |
| self._day_start = time.time() |
| self._total_calls = 0 |
| self._throttled = 0 |
|
|
| def _reset_windows(self): |
| now = time.time() |
| if now - self._minute_start > 60: |
| self._minute.clear() |
| self._minute_start = now |
| if now - self._hour_start > 3600: |
| self._hour.clear() |
| self._hour_start = now |
| if now - self._day_start > 86400: |
| self._day.clear() |
| self._day_start = now |
|
|
| def can_call(self, provider: str) -> tuple[bool, str]: |
| """Check if we can make a call to this provider without exceeding limits.""" |
| self._reset_windows() |
| limits = PROVIDER_LIMITS.get(provider, {}) |
| if not limits: |
| return True, "" |
|
|
| |
| rpm = limits.get("rpm", 20) |
| if self._minute[provider] >= rpm: |
| wait = 60 - (time.time() - self._minute_start) |
| return False, f"{provider}: RPM limit ({rpm}/min), retry in {wait:.0f}s" |
|
|
| |
| if provider == "openrouter": |
| rpd = limits.get("rpd_free_with_credits", 1000) |
| elif provider == "groq": |
| rpd = limits.get("rpd", 14400) |
| else: |
| rpd = limits.get("rpd", 100000) |
|
|
| if self._day[provider] >= rpd: |
| return False, f"{provider}: Daily limit ({rpd}/day) exhausted" |
|
|
| |
| if provider == "mistral" and limits.get("rps", 1): |
| if self._minute[provider] >= 58: |
| return False, "mistral: nearing RPS limit" |
|
|
| return True, "" |
|
|
| def record_call(self, provider: str, tokens: int = 0): |
| """Record a successful API call.""" |
| self._reset_windows() |
| self._minute[provider] += 1 |
| self._hour[provider] += 1 |
| self._day[provider] += 1 |
| self._total_calls += 1 |
|
|
| def record_throttle(self, provider: str): |
| """Record a throttled/blocked call.""" |
| self._throttled += 1 |
|
|
| def budget_remaining(self, provider: str) -> dict: |
| """Get remaining budget for a provider.""" |
| self._reset_windows() |
| limits = PROVIDER_LIMITS.get(provider, {}) |
| rpm = limits.get("rpm", 20) |
|
|
| if provider == "openrouter": |
| rpd = limits.get("rpd_free_with_credits", 1000) |
| elif provider == "groq": |
| rpd = limits.get("rpd", 14400) |
| else: |
| rpd = 100000 |
|
|
| return { |
| "provider": provider, |
| "minute_used": self._minute[provider], |
| "minute_limit": rpm, |
| "minute_remaining": max(0, rpm - self._minute[provider]), |
| "day_used": self._day[provider], |
| "day_limit": rpd, |
| "day_remaining": max(0, rpd - self._day[provider]), |
| "day_pct": round(self._day[provider] / max(rpd, 1) * 100, 1), |
| } |
|
|
| def stats(self) -> dict: |
| """Full usage statistics.""" |
| return { |
| "total_calls": self._total_calls, |
| "throttled": self._throttled, |
| "providers": {p: self.budget_remaining(p) for p in PROVIDER_LIMITS}, |
| "budget_allocation": { |
| "daily_intel": "3-5 calls/day", |
| "ct_rundown": "1-2 calls/day", |
| "content_review": "5-10 calls/day", |
| "background": "10-20 calls/day", |
| "headroom": f"~{1000 - self._day.get('openrouter', 0)} calls remaining today", |
| }, |
| } |
|
|
|
|
| |
| rate_tracker = RateLimitTracker() |
|
|
|
|
| def _can_use(model_config: dict) -> bool: |
| """Check if model is under its rate limit using the tracker.""" |
| provider = model_config.get("provider", "openrouter") |
| can, reason = rate_tracker.can_call(provider) |
| if not can: |
| logger.debug(f"Rate limited: {reason}") |
| rate_tracker.record_throttle(provider) |
| return False |
| return True |
|
|
|
|
| def _track_usage(model_id: str, tokens: int = 0): |
| """Track model usage through the rate tracker.""" |
| for _provider, _limits in PROVIDER_LIMITS.items(): |
| |
| model_providers = { |
| "openrouter": [ |
| "nvidia/", |
| "z-ai/", |
| "google/", |
| "qwen/", |
| "openai/", |
| "moonshotai/", |
| "liquid/", |
| "openrouter/", |
| ], |
| "groq": ["llama-3", "llama-4", "mixtral", "gemma"], |
| "mistral": ["mistral", "ministral", "codestral", "open-mistral"], |
| } |
| for p, prefixes in model_providers.items(): |
| if any(model_id.startswith(pref) for pref in prefixes): |
| rate_tracker.record_call(p, tokens) |
| return |
|
|
|
|
| async def _call_openrouter( |
| model_id: str, system: str, user: str, max_tokens: int = 1000, temperature: float = 0.5 |
| ) -> str: |
| """Call OpenRouter API.""" |
| if not OPENROUTER_KEY: |
| return "" |
|
|
| try: |
| async with httpx.AsyncClient(timeout=90) as c: |
| r = await c.post( |
| OR_URL, |
| headers={ |
| "Authorization": f"Bearer {OPENROUTER_KEY}", |
| "Content-Type": "application/json", |
| "HTTP-Referer": "https://rugmunch.io", |
| "X-Title": "RugCharts AI", |
| }, |
| json={ |
| "model": model_id, |
| "temperature": temperature, |
| "max_tokens": max_tokens, |
| "messages": [ |
| {"role": "system", "content": system}, |
| {"role": "user", "content": user}, |
| ], |
| }, |
| ) |
| if r.status_code == 200: |
| resp = r.json() |
| usage = resp.get("usage", {}) |
| _track_usage(model_id, usage.get("total_tokens", 0)) |
| return resp["choices"][0]["message"]["content"] |
| else: |
| logger.warning(f"OpenRouter {model_id}: {r.status_code}") |
| return "" |
| except Exception as e: |
| logger.warning(f"OpenRouter error {model_id}: {e}") |
| return "" |
|
|
|
|
| async def _call_groq(model_id: str, system: str, user: str, max_tokens: int = 1000, temperature: float = 0.5) -> str: |
| """Call Groq API (free tier).""" |
| if not GROQ_KEY: |
| return "" |
|
|
| try: |
| async with httpx.AsyncClient(timeout=60) as c: |
| r = await c.post( |
| GROQ_URL, |
| headers={"Authorization": f"Bearer {GROQ_KEY}", "Content-Type": "application/json"}, |
| json={ |
| "model": model_id, |
| "temperature": temperature, |
| "max_tokens": max_tokens, |
| "messages": [ |
| {"role": "system", "content": system}, |
| {"role": "user", "content": user}, |
| ], |
| }, |
| ) |
| if r.status_code == 200: |
| return r.json()["choices"][0]["message"]["content"] |
| else: |
| logger.warning(f"Groq {model_id}: {r.status_code} {r.text[:200]}") |
| return "" |
| except Exception as e: |
| logger.warning(f"Groq error: {e}") |
| return "" |
|
|
|
|
| async def _call_mistral(model_id: str, system: str, user: str, max_tokens: int = 1000, temperature: float = 0.5) -> str: |
| """Call Mistral API (free tier).""" |
| if not MISTRAL_KEY: |
| return "" |
|
|
| try: |
| async with httpx.AsyncClient(timeout=60) as c: |
| r = await c.post( |
| MISTRAL_URL, |
| headers={ |
| "Authorization": f"Bearer {MISTRAL_KEY}", |
| "Content-Type": "application/json", |
| }, |
| json={ |
| "model": model_id, |
| "temperature": temperature, |
| "max_tokens": max_tokens, |
| "messages": [ |
| {"role": "system", "content": system}, |
| {"role": "user", "content": user}, |
| ], |
| }, |
| ) |
| if r.status_code == 200: |
| _track_usage(model_id, max_tokens) |
| return r.json()["choices"][0]["message"]["content"] |
| else: |
| logger.warning(f"Mistral {model_id}: {r.status_code}") |
| return "" |
| except Exception as e: |
| logger.warning(f"Mistral error: {e}") |
| return "" |
|
|
|
|
| async def ai_call( |
| task_type: str, |
| system_prompt: str, |
| user_prompt: str, |
| max_tokens: int = 1000, |
| temperature: float = 0.5, |
| ) -> str: |
| """THE method. Call the best free model for a task type. |
| |
| Routes to: research, writing, coding, review, fast. |
| Falls back: primary β fallback β groq β mistral β any available. |
| Three providers: OpenRouter (3 models), Groq (2 models), Mistral (6 models). |
| Zero cost. Always finds a model. |
| """ |
| if task_type not in MODELS: |
| task_type = "fast" |
|
|
| config = MODELS[task_type] |
|
|
| |
| tiers = ["primary", "fallback", "groq"] |
|
|
| for tier in tiers: |
| if tier not in config: |
| continue |
| model = config[tier] |
| if not _can_use(model): |
| continue |
|
|
| if model["provider"] == "openrouter": |
| result = await _call_openrouter(model["id"], system_prompt, user_prompt, max_tokens, temperature) |
| elif model["provider"] == "groq": |
| result = await _call_groq(model["id"], system_prompt, user_prompt, max_tokens, temperature) |
| elif model["provider"] == "mistral": |
| result = await _call_mistral(model["id"], system_prompt, user_prompt, max_tokens, temperature) |
| else: |
| continue |
|
|
| if result: |
| return result |
|
|
| |
| mistral_tasks = ["mistral_fast", "mistral_write", "mistral_code"] |
| for mt in mistral_tasks: |
| if mt == task_type: |
| continue |
| mconfig = MODELS.get(mt, {}) |
| for tier in ["primary", "fallback"]: |
| if tier not in mconfig: |
| continue |
| model = mconfig[tier] |
| if _can_use(model): |
| result = await _call_mistral(model["id"], system_prompt, user_prompt, max_tokens, temperature) |
| if result: |
| return result |
|
|
| |
| for backup_type in ["fast", "writing", "writing_groq"]: |
| if backup_type == task_type: |
| continue |
| backup_config = MODELS[backup_type] |
| for tier_name in ["primary", "fallback", "groq"]: |
| if tier_name in backup_config: |
| model = backup_config[tier_name] |
| if _can_use(model): |
| if model["provider"] == "openrouter": |
| result = await _call_openrouter( |
| model["id"], system_prompt, user_prompt, max_tokens, temperature |
| ) |
| elif model["provider"] == "groq": |
| result = await _call_groq(model["id"], system_prompt, user_prompt, max_tokens, temperature) |
| elif model["provider"] == "mistral": |
| result = await _call_mistral(model["id"], system_prompt, user_prompt, max_tokens, temperature) |
| if result: |
| return result |
|
|
| return "" |
|
|
|
|
| |
| |
| |
|
|
| FORBIDDEN_WORDS = [ |
| "delve", |
| "tapestry", |
| "landscape", |
| "robust", |
| "moreover", |
| "furthermore", |
| "in conclusion", |
| "it is worth noting", |
| "underscores", |
| "showcasing", |
| "a testament to", |
| "in the realm of", |
| "paradigm shift", |
| "game changer", |
| "revolutionize", |
| "disrupt", |
| "unprecedented", |
| "groundbreaking", |
| "synergy", |
| "ecosystem", |
| "holistic", |
| "cutting-edge", |
| "state-of-the-art", |
| "leveraging", |
| "utilize", |
| "facilitate", |
| "spearhead", |
| ] |
|
|
| QUALITY_REVIEW_PROMPT = """You are a ruthless editor at RugCharts. Review this content against STRICT standards: |
| |
| FORBIDDEN (mark as FAIL if found): |
| - "delve", "tapestry", "landscape", "robust", "moreover", "furthermore" |
| - "in conclusion", "it is worth noting", "underscores", "showcasing" |
| - "a testament to", "in the realm of", "paradigm shift" |
| - Any vague, corporate, or AI-slop language |
| - Overused crypto clichΓ©s ("to the moon", "wagmi", "ngmi", "wen") |
| |
| REQUIRED (mark as FAIL if missing): |
| - Specific numbers, names, percentages |
| - Human, conversational tone (reads like a sharp newsletter) |
| - No passive voice where active works better |
| - Short paragraphs. Varied sentence length. |
| - Hooks the reader in first 2 sentences |
| |
| OUTPUT FORMAT β JSON only: |
| { |
| "pass": true/false, |
| "score": 0-100, |
| "issues": ["list of specific problems found"], |
| "fixed_version": "rewritten version if score < 80, otherwise original" |
| } |
| |
| CONTENT TO REVIEW: |
| """ |
|
|
|
|
| async def review_content(content: str, content_type: str = "article") -> dict: |
| """Review content against quality standards. Returns pass/fail with fixes.""" |
| if len(content) < 50: |
| return {"pass": True, "score": 100, "issues": [], "fixed_version": content} |
|
|
| |
| issues = [] |
| content_lower = content.lower() |
|
|
| for word in FORBIDDEN_WORDS: |
| if word in content_lower: |
| issues.append(f"Forbidden word: '{word}'") |
|
|
| |
| slop_patterns = [ |
| (r"it is (worth|important|crucial|essential) to", "AI-slop: 'it is X to'"), |
| (r"in (conclusion|summary|essence)", "AI-slop: 'in X'"), |
| (r"as we (have|can) seen", "AI-slop: 'as we have seen'"), |
| (r"plays? a (crucial|vital|key|important) role", "AI-slop: 'plays a X role'"), |
| ] |
|
|
| import re |
|
|
| for pattern, label in slop_patterns: |
| if re.search(pattern, content_lower): |
| issues.append(label) |
|
|
| |
| base_score = 100 |
| base_score -= len(issues) * 8 |
| |
| if len(content) < 300: |
| base_score -= 15 |
| |
| paragraphs = [p for p in content.split("\n\n") if len(p) > 50] |
| if paragraphs: |
| avg_para_len = sum(len(p) for p in paragraphs) / len(paragraphs) |
| if avg_para_len > 500: |
| base_score -= 10 |
| issues.append("Paragraphs too long (avg >500 chars)") |
|
|
| |
| if base_score < 85 and len(issues) > 1: |
| try: |
| ai_review = await ai_call("review", QUALITY_REVIEW_PROMPT, content, max_tokens=800, temperature=0.2) |
| if ai_review: |
| try: |
| review_data = json.loads(ai_review.strip().lstrip("```json").rstrip("```")) |
| issues.extend(review_data.get("issues", [])) |
| if review_data.get("score", 100) < base_score: |
| base_score = review_data["score"] |
| if not review_data.get("pass", True): |
| return { |
| "pass": False, |
| "score": base_score, |
| "issues": issues, |
| "fixed_version": review_data.get("fixed_version", content), |
| } |
| except Exception: |
| pass |
| except Exception: |
| pass |
|
|
| |
| fixed = content |
| if base_score < 70: |
| try: |
| fix_prompt = f"""Rewrite this content to meet quality standards. Remove all AI-slop language, forbidden words, and corporate speak. Make it human, direct, and specific. |
| |
| Current issues: {", ".join(issues[:5])} |
| |
| ORIGINAL: |
| {content[:2000]}""" |
| fixed = await ai_call( |
| "writing", |
| "You are a skilled human writer. Rewrite content to be direct, specific, and natural. No AI-slop.", |
| fix_prompt, |
| max_tokens=len(content) // 2 + 500, |
| temperature=0.4, |
| ) |
| if not fixed: |
| fixed = content |
| except Exception: |
| fixed = content |
|
|
| return { |
| "pass": base_score >= 70, |
| "score": max(0, min(100, base_score)), |
| "issues": issues[:10], |
| "fixed_version": fixed, |
| } |
|
|
|
|
| |
| |
| |
|
|
|
|
| def build_research_prompt(topic: str, data: dict | None = None) -> str: |
| """Build a research prompt with all available context.""" |
| parts = [f"Research task: {topic}\n"] |
|
|
| if data: |
| for key, value in data.items(): |
| if isinstance(value, str): |
| parts.append(f"## {key.upper()}\n{value[:2000]}") |
| elif isinstance(value, list): |
| parts.append(f"## {key.upper()}\n" + "\n".join(f"- {str(v)[:200]}" for v in value[:10])) |
| elif isinstance(value, dict): |
| parts.append(f"## {key.upper()}\n{json.dumps(value, default=str)[:1000]}") |
|
|
| return "\n\n".join(parts) |
|
|
|
|
| def build_writing_prompt(topic: str, research_notes: str, style: str = "newsletter") -> str: |
| """Build a writing prompt from research notes.""" |
| return f"""Write a {style} about: {topic} |
| |
| RESEARCH NOTES: |
| {research_notes[:3000]} |
| |
| Style guide: |
| - Direct, human voice. No corporate speak. No AI-slop. |
| - Lead with the most interesting detail. |
| - Use specific numbers, names, facts. |
| - Vary sentence length. Short paragraphs. |
| - End with a clear takeaway. |
| |
| Write the complete piece now:""" |
|
|
|
|
| def get_usage_stats() -> dict: |
| """Get current model usage statistics from rate tracker.""" |
| return rate_tracker.stats() |
|
|