Diagnose and fix broken LLM model configurations in CogniForge microservices. Use when: AI responses are empty, garbage, or missing LaTeX; reasoning-agent returns empty answers or times out; research-agent returns empty results; any service logs "rate-limited", "429", or "Provider returned error"; a model default needs updating after OpenRouter deprecates a free model; content=None from any model; reasoning-only model used as PRIMARY. Triggers on: "إجابات فارغة", "إجابات كارثية", "rate-limited", "429", "Provider returned error", "No endpoints found", "empty answer", "model broken", "reasoning timeout", "inclusionai", "ring-2.6", "model not working", "LLM not responding", "بدون LaTeX", "إجابات غبية", "نموذج معطّل", "content=None", "reasoning model", "omni-reasoning", "إجابات غير منظمة", "حروف متداخلة", "فقدان سياق", "نصوص غير منظمة".
Resources
2Install
npx skillscat add bakabala27-svg/naas-agentic-core/cogniforge-llm-model-repair Install via the SkillsCat registry.
CogniForge LLM Model Repair
Law: A model is ACTIVE only when it returns Arabic text + LaTeX in < 30s
ANDmessage.contentis non-None and non-empty with a system prompt.
A running service ≠ a working LLM. Always verify with a live math question.
1. Rapid Diagnosis (60 seconds)
# Step 1: Check what model each service is using
grep -rn "OPENROUTER_MODEL\|DEFAULT_MODEL\|AI_MODEL\|primary_model\|self.model" \
app/core/ai_config.py \
microservices/reasoning_agent/src/ai_client.py \
microservices/reasoning_agent/src/core/config.py \
microservices/planning_agent/settings.py \
| grep -v __pycache__ | grep -v "^.*#"
# Step 2: Scan for banned models as active values
grep -rn "inclusionai/ring-2.6-1t:free\|nemotron-3-nano-omni-30b-a3b-reasoning:free" \
--include="*.py" app/ microservices/ \
| grep -v __pycache__ | grep -v "^.*#" | grep -v test_
# Step 3: Test content=None bug (ISS-069)
python3 -c "
import asyncio, httpx, os
async def test():
async with httpx.AsyncClient() as c:
r = await c.post('https://openrouter.ai/api/v1/chat/completions',
headers={'Authorization': f'Bearer {os.environ[\"OPENROUTER_API_KEY\"]}'},
json={'model': 'nvidia/nemotron-3-nano-30b-a3b:free',
'messages': [{'role': 'system', 'content': 'أستاذ رياضيات.'},
{'role': 'user', 'content': 'احسب 2+2'}],
'max_tokens': 50}, timeout=15)
msg = r.json()['choices'][0]['message']
content = msg.get('content')
print('content:', repr(content))
assert content is not None and len(content) > 0, 'BROKEN: content=None'
print('OK: content is valid')
asyncio.run(test())
"
# Step 4: Live model test (30s)
python3 scripts/benchmark_models.pySymptom → Root Cause mapping:
| Symptom | Root Cause |
|---|---|
reasoning-agent returns {"answer": ""} |
Model rate-limited OR content=None bug |
content=None in any service |
Reasoning-only model used as PRIMARY (ISS-069) |
| إجابات فارغة مع system prompt | nemotron-3-nano-omni-30b-a3b-reasoning:free كـ PRIMARY |
research-agent returns {"results": []} |
Tavily key missing OR model broken |
429 in service logs |
Model rate-limited upstream |
Provider returned error |
Model endpoint removed from OpenRouter |
No endpoints found |
Model no longer available on OpenRouter |
| Response has no LaTeX | System prompt missing LaTeX instruction |
| Response in English only | System prompt not enforcing Arabic |
| MCTS timeout (>45s) | depth > 1 with free model |
2. Live Model Benchmark
Run scripts/benchmark_models.py to find working models. See references/model-registry.md for the current verified model list.
Quick single-model test:
import httpx, asyncio, time
async def test(model, api_key):
headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'}
t0 = time.time()
async with httpx.AsyncClient(timeout=25) as c:
r = await c.post('https://openrouter.ai/api/v1/chat/completions',
headers=headers,
json={
'model': model,
'messages': [
{'role': 'system', 'content': 'أنت أستاذ رياضيات. أجب بالعربية مع LaTeX.'},
{'role': 'user', 'content': 'ما هو مشتق ln(x)؟'}
],
'max_tokens': 150
})
d = r.json()
choices = d.get('choices', [])
if choices:
content = choices[0].get('message', {}).get('content', '') or ''
has_latex = '$$' in content or '\\[' in content or '\\(' in content
has_arabic = any('\u0600' <= c <= '\u06ff' for c in content)
print(f'✅ {time.time()-t0:.1f}s | LaTeX:{has_latex} | AR:{has_arabic} | {model}')
else:
print(f'❌ {model}: {d.get("error",{}).get("message","?")}')
asyncio.run(test('nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free', 'YOUR_KEY'))3. Replace Broken Model (Surgical Fix)
When a model is confirmed broken, replace it in all 14 locations. Run scripts/replace_model.py for automated replacement, or do it manually:
Files to update (in order):
app/core/ai_config.py ← PRIMARY + fallback chain
app/services/chat/local_graph.py ← system prompts
app/services/chat/agents/orchestrator.py ← hardcoded model
app/services/chat/agents/socratic_tutor.py ← hardcoded model
microservices/reasoning_agent/src/ai_client.py ← OPENROUTER_MODEL default
microservices/reasoning_agent/src/core/config.py ← DEFAULT_MODEL
microservices/reasoning_agent/src/services/reasoning_service.py ← timeout + depth
microservices/reasoning_agent/src/services/strategies/mcts.py ← prompts
microservices/research_agent/src/search_engine/super_search.py ← PRIMARY_MODEL
microservices/research_agent/src/search_engine/query_refiner.py ← default model
microservices/planning_agent/settings.py ← AI_MODEL
microservices/orchestrator_service/src/core/ai_config.py ← AvailableModels
microservices/orchestrator_service/src/services/llm/client.py ← default_model
microservices/orchestrator_service/src/services/overmind/agents/orchestrator.py
microservices/orchestrator_service/src/services/overmind/graph/main.py ← DSPy model
microservices/conversation_service/src/conversation_graph.py ← model
microservices/auditor_service/src/ai.py ← modelVerify no banned model remains:
grep -rn "inclusionai/ring-2.6-1t:free" --include="*.py" app/ microservices/ \
| grep -v __pycache__ | grep -v "^.*#" | grep -v test_
# Must return empty4. Restart Services with New Model
export OPENROUTER_API_KEY="..."
export TAVILY_API_KEY="..."
export PYTHONPATH="/workspaces/NAAS-Agentic-Core"
# Kill old instances
kill $(pgrep -f "reasoning_agent.main:app") 2>/dev/null
kill $(pgrep -f "research_agent.main:app") 2>/dev/null
kill $(pgrep -f "planning_agent.main:app") 2>/dev/null
sleep 3
# Restart with explicit model env vars
OPENROUTER_MODEL="nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free" \
OPENROUTER_API_KEY="$OPENROUTER_API_KEY" \
PYTHONPATH="$PYTHONPATH" \
nohup python -m uvicorn microservices.reasoning_agent.main:app \
--host 0.0.0.0 --port 8008 --log-level warning > /tmp/reasoning.log 2>&1 &
# Verify
sleep 8
curl -s http://localhost:8008/health
# Expected: {"llm_backend":"openrouter","mcts_enabled":"true"}5. Fix System Prompts for Arabic Math
Load references/system-prompt-templates.md for the canonical prompt templates.
Mandatory elements in every educational prompt:
$$...$$for standalone equations\(...\)for inline symbols$$\boxed{...}$$for final results- Numbered steps with mathematical principle explanation
- Arabic فصحى only (no English except technical terms)
- Geometric/physical interpretation when applicable
Quick check:
python3 -c "
content = open('app/services/chat/local_graph.py').read()
checks = ['\$\$', 'boxed', 'LaTeX', 'educational', 'general', 'chat']
for c in checks:
print(f'{'✅' if c in content else '❌'} {c}')
"6. Fix MCTS for Free Models
Free models rate-limit at depth > 1. Always use depth=1.
# microservices/reasoning_agent/src/services/reasoning_service.py
class ReasoningWorkflow(Workflow):
# depth=1 prevents rate-limit cascade with free models
def __init__(self, timeout: int = 45, verbose: bool = True):
super().__init__(timeout=timeout, verbose=verbose)
# In the reason step:
best_node = await self.strategy.execute(
root_content=f"Analyze: {query}",
context=context,
depth=1 # NOT 2 — causes 6+ LLM calls → rate limiting
)7. Verify End-to-End
python3 << 'EOF'
import httpx, asyncio, time
async def verify():
# Health matrix
for port, name in [(8000,'main'), (8001,'user'), (8002,'planning'),
(8007,'research'), (8008,'reasoning')]:
r = await httpx.AsyncClient(timeout=4).get(f'http://localhost:{port}/health')
d = r.json()
status = d.get('status') or d.get('application', '?')
print(f' {"✅" if status in ["ok","healthy"] else "❌"} :{port} {name}: {status}')
# Live reasoning test
t0 = time.time()
r = await httpx.AsyncClient(timeout=50).post('http://localhost:8008/execute', json={
'caller_id': 'verify', 'action': 'reason',
'query': 'ما هو مشتق ln(x)؟',
'payload': {'query': 'ما هو مشتق ln(x)؟', 'context': 'رياضيات'}
})
d = r.json()
answer = d.get('data', {}).get('answer', '')
has_latex = '\\[' in answer or '$$' in answer or '\\(' in answer
print(f' {"✅" if d.get("status")=="success" else "❌"} reasoning ({time.time()-t0:.1f}s) | LaTeX:{has_latex}')
asyncio.run(verify())
EOF8. Update Memory After Fix
After any model change, update these files:
CLAUDE.md— add ISS-NNN entry with benchmark results and permanent rule.memory/issues.md— full diagnosis + fix + files changed.memory/runtime_truth.md— D-NNN live verification table.memory/decisions.md— decision record with rationale and invariants
See references/memory-update-template.md for the exact format.
Anti-patterns
- Never trust service logs alone — always probe
/healthAND test with a live math question. - Never set MCTS depth > 1 with free models — causes rate-limit cascade.
- Never use
inclusionai/ring-2.6-1t:free— permanently rate-limited on Novita (ISS-068). - Never use
nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:freeas PRIMARY — content=None with system prompt (ISS-069). - Never use any model ending in
:reasoning:freeas PRIMARY without verifyingmessage.contentis non-None with a system prompt. - Never hardcode a model string without an env var override — use
os.getenv("MODEL_VAR", "default"). - Never assume a model works because it appears in OpenRouter's model list — test it live with a system prompt.
- Never skip the memory update — the next agent session will repeat the same diagnosis.
- Never accept
content=Nonesilently — always fallback toreasoningfield and log a warning.