purge: remove ALL hardcoded model/URL defaults across 11 modules

TDD cycle (RED → GREEN):

2 new codebase-wide enforcement tests:
- test_no_getattr_with_hardcoded_model_defaults_outside_config
  Catches getattr(args, 'ai_xxx', 'HARDCODED_DEFAULT') anti-pattern
- test_no_bare_localhost_url_string_literals_outside_config
  Catches bare 'localhost:11434' string literals in non-SSOT files

Purged 24 getattr-default violations and 14 bare-URL violations across:
- compiler_engine.py: ai_telepathic_model/url defaults
- screen_identity.py: ai_telepathic_model/url defaults
- intent_resolver.py: ai_telepathic_model/url defaults (2 spots)
- semantic_evaluator.py: ai_telepathic_model/url defaults
- brain.py: ai_model/url defaults
- dm_engine.py: ai_condenser_model/url defaults
- resonance_engine.py: ai_condenser_model/url defaults
- qdrant_memory.py: ai_embedding_model/url defaults
- bot_flow.py: ai_condenser_model/url defaults
- interaction.py: ai_writer_model/url defaults
- llm_provider.py: fallback model/url 'last resort' defaults

Architecture: Config() (config.py) is the SSOT for ALL AI model config.
Every other module reads from Config().args WITHOUT default values.
If Config() is uninitialized, the system crashes (Fail Fast).

Tests: 11 new, 121 passing, 0 regressions
This commit is contained in:
2026-05-04 00:28:40 +02:00
parent c98e2caaa1
commit 720841103d
14 changed files with 131 additions and 46 deletions

View File

@@ -444,8 +444,8 @@ def start_bot(**kwargs):
from GramAddict.core.llm_provider import query_llm
model = getattr(configs.args, "ai_condenser_model", "llama3.2:1b")
url = getattr(configs.args, "ai_condenser_url", "http://localhost:11434/api/generate")
model = getattr(configs.args, "ai_condenser_model")
url = getattr(configs.args, "ai_condenser_url")
response_dict = query_llm(url=url, model=model, prompt=prompt, format_json=True, timeout=120)
if response_dict and isinstance(response_dict, dict) and "persona" in response_dict:

View File

@@ -30,13 +30,13 @@ class VLMCompilerEngine:
extra={"color": "\x1b[1m\x1b[35m"},
)
args = getattr(self.device, "args", None)
model = getattr(args, "ai_telepathic_model", "llama3.2:1b") if args else "llama3.2:1b"
url = (
getattr(args, "ai_telepathic_url", "http://localhost:11434/api/generate")
if args
else "http://localhost:11434/api/generate"
)
from GramAddict.core.config import Config
cfg = Config()
if not hasattr(cfg, "args"):
raise RuntimeError("Config().args not initialized — cannot resolve AI model. Fail Fast.")
model = getattr(cfg.args, "ai_telepathic_model")
url = getattr(cfg.args, "ai_telepathic_url")
use_local = "11434" in url or "localhost" in url
simplified_xml = self._simplify_xml(context_xml)

View File

@@ -152,8 +152,8 @@ def _run_zero_latency_dm_loop(device, zero_engine, nav_graph, configs, session_s
return "BOREDOM_CHANGE_FEED"
# Configure models
model = getattr(configs.args, "ai_condenser_model", "llama3.2:1b")
url = getattr(configs.args, "ai_condenser_url", "http://localhost:11434/api/generate")
model = getattr(configs.args, "ai_condenser_model")
url = getattr(configs.args, "ai_condenser_url")
# Generate response
prompt = f"You are replying to a direct message on Instagram. The last message you received was: '{context_text}'. Keep it short, casual, and friendly. Do not use hashtags."

View File

@@ -54,9 +54,9 @@ class LLMWriter:
f"6. Reply with ONLY the comment text."
)
model = getattr(self.args, "ai_writer_model", getattr(self.args, "ai_model", "llama3.2:1b"))
model = getattr(self.args, "ai_writer_model", getattr(self.args, "ai_model"))
url = getattr(
self.args, "ai_writer_url", getattr(self.args, "ai_model_url", "http://localhost:11434/api/generate")
self.args, "ai_writer_url", getattr(self.args, "ai_model_url")
)
logger.info(f"✍️ [Writer] Generating comment for @{target_username} using {model}...")

View File

@@ -395,14 +395,11 @@ def query_llm(
except Exception:
pass
# Last resort defaults
# Last resort: If no fallback config exists, don't silently use hardcoded defaults.
# Config() defines these via argparse defaults — if they're missing, there's nothing to fallback to.
if not f_model or not f_url:
if is_openai_compat:
f_model = f_model or "llama3.2:1b"
f_url = f_url or "http://localhost:11434/api/generate"
else:
f_model = f_model or "llama3.2:1b"
f_url = f_url or "http://localhost:11434/api/generate"
logger.warning("⚠️ [Circuit Breaker] No fallback model/URL configured. Cannot retry.")
return None
# Circuit Breaker: If fallback is identical to primary, don't waste time retrying
if f_model == model and f_url == url:
@@ -458,11 +455,12 @@ def query_telepathic_llm(
try:
args = Config().args
target_url = getattr(args, "ai_fallback_url", "http://localhost:11434/api/generate")
target_model = getattr(args, "ai_fallback_model", "llama3.2:1b")
target_url = getattr(args, "ai_fallback_url")
target_model = getattr(args, "ai_fallback_model")
except Exception:
target_url = "http://localhost:11434/api/generate"
target_model = "llama3.2:1b"
raise RuntimeError(
"Config().args not initialized — cannot resolve fallback AI model. Fail Fast."
)
is_local = "localhost" in target_url or "127.0.0.1" in target_url
calc_timeout = 180 if is_local else 45

View File

@@ -15,12 +15,10 @@ def ask_brain_for_action(
return None
cfg = Config()
url = (
getattr(cfg.args, "ai_model_url", "http://localhost:11434/api/generate")
if hasattr(cfg, "args")
else "http://localhost:11434/api/generate"
)
model = getattr(cfg.args, "ai_model", "qwen3.5:latest") if hasattr(cfg, "args") else "qwen3.5:latest"
if not hasattr(cfg, "args"):
raise RuntimeError("Config().args not initialized — cannot resolve AI model. Fail Fast.")
url = getattr(cfg.args, "ai_model_url")
model = getattr(cfg.args, "ai_model")
prompt = (
f"You are an autonomous Instagram agent. Your ultimate goal is: '{goal}'.\n"

View File

@@ -661,8 +661,8 @@ class IntentResolver:
self.last_box_map = box_map
cfg = Config()
model = getattr(cfg.args, "ai_telepathic_model", "llava:latest")
url = getattr(cfg.args, "ai_telepathic_url", "http://localhost:11434/api/generate")
model = getattr(cfg.args, "ai_telepathic_model")
url = getattr(cfg.args, "ai_telepathic_url")
# Build a compact legend of what each box contains
box_legend_lines = []
@@ -785,8 +785,8 @@ class IntentResolver:
filtered_candidates = [n for n in candidates if n.area < 500000]
cfg = Config()
model = getattr(cfg.args, "ai_telepathic_model", "qwen3.5:latest")
url = getattr(cfg.args, "ai_telepathic_url", "http://localhost:11434/api/generate")
model = getattr(cfg.args, "ai_telepathic_model")
url = getattr(cfg.args, "ai_telepathic_url")
node_context = []
for i, node in enumerate(filtered_candidates):

View File

@@ -276,12 +276,10 @@ class ScreenIdentity:
from GramAddict.core.llm_provider import query_telepathic_llm
cfg = Config()
url = (
getattr(cfg.args, "ai_telepathic_url", "http://localhost:11434/api/generate")
if hasattr(cfg, "args")
else "http://localhost:11434/api/generate"
)
model = getattr(cfg.args, "ai_telepathic_model", "llava:latest") if hasattr(cfg, "args") else "llava:latest"
if not hasattr(cfg, "args"):
raise RuntimeError("Config().args not initialized — cannot resolve AI model. Fail Fast.")
url = getattr(cfg.args, "ai_telepathic_url")
model = getattr(cfg.args, "ai_telepathic_model")
layout_context = (
f"Selected Tab: {selected_tab}\nResource IDs: {list(ids)}\nVisible Texts context: {texts[:10]}\n"

View File

@@ -28,8 +28,8 @@ class SemanticEvaluator:
logger.warning("👁️ [Vision Core] No config available. Cannot query VLM.")
return None
model = getattr(self.args, "ai_telepathic_model", "llama3.2-vision")
url = getattr(self.args, "ai_telepathic_url", "http://localhost:11434/api/generate")
model = getattr(self.args, "ai_telepathic_model")
url = getattr(self.args, "ai_telepathic_url")
try:
res = query_telepathic_llm(

View File

@@ -112,8 +112,8 @@ class QdrantBase:
args = self._cached_args
# Pull specific embedding config or fallback to defaults
model = getattr(args, "ai_embedding_model", "nomic-embed-text")
url = getattr(args, "ai_embedding_url", "http://localhost:11434/api/embeddings")
model = getattr(args, "ai_embedding_model")
url = getattr(args, "ai_embedding_url")
try:
# Generate embeddings

View File

@@ -386,8 +386,8 @@ class ResonanceEngine:
"Set 'keep' to false only for clear spam, bots, UI buttons, or blacklist violations.\n"
)
model = getattr(configs.args, "ai_condenser_model", "llama3.2:1b")
url = getattr(configs.args, "ai_condenser_url", "http://localhost:11434/api/generate")
model = getattr(configs.args, "ai_condenser_model")
url = getattr(configs.args, "ai_condenser_url")
try:
import json