import re with open("app/modules/manual/backend/router.py", "r") as f: text = f.read() # Make sure to import cache if "from app.modules.manual.backend.cache import manual_cache" not in text: text = text.replace("from app.core.database import", "from app.modules.manual.backend.cache import manual_cache\nfrom app.core.database import") # Patch GET list: # It has def get_manual_articles( ... ) -> Dict[str, Any]: # Need to inject at the top of that function. patch_list = """async def get_manual_articles( module: Optional[str] = Query(None, description="Filtrer på modul"), difficulty: Optional[DifficultyType] = Query(None, description="Filtrer på sværhedsgrad"), tag: Optional[str] = Query(None, description="Filtrer på et bestemt tag"), search: Optional[str] = Query(None, description="Søg i titel, summary og content"), limit: int = Query(50, ge=1, le=100), offset: int = Query(0, ge=0), ) -> Dict[str, Any]: cache_key = f"list:{module}:{difficulty}:{tag}:{search}:{limit}:{offset}" cached = manual_cache.get(cache_key) if cached: return cached""" text = re.sub( r'async def get_manual_articles\([^)]*\)\s*->\s*Dict\[str, Any\]:', patch_list, text ) # And at the end of get_manual_articles: # return {"items": rows, "total": total} # Replace with setting cache text = text.replace( 'return {"items": rows, "total": total}', 'result = {"items": rows, "total": total}\n manual_cache.set(cache_key, result)\n return result' ) # Patch Context: patch_context = """async def contextual_manual_suggestions( module: str = Query(..., description="Modulet der søges hjælp ud fra, fx 'Sag'"), tag: Optional[str] = Query(None, description="Kategori eller tag fra konteksten"), limit: int = Query(5, ge=1, le=10) ): cache_key = f"context:{module}:{tag}:{limit}" cached = manual_cache.get(cache_key) if cached: return cached""" text = re.sub( r'async def contextual_manual_suggestions\([^)]*\):', patch_context, text ) # return {"results": rows} text = text.replace( 'return {"results": rows}', 'result = {"results": rows}\n manual_cache.set(cache_key, result)\n return result' ) # Patch Get Slug: patch_slug = """async def get_manual_article(slug: str, background_tasks: BackgroundTasks): cache_key = f"slug:{slug}" cached = manual_cache.get(cache_key) if cached: background_tasks.add_task(_increment_use_count, cached["id"]) return cached""" text = re.sub( r'async def get_manual_article\(slug: str.*?BackgroundTasks[^)]*\):', patch_slug, text ) text = re.sub( r'return \{\s*"article": article,\s*"steps": steps,\s*"relations": related\s*\}', 'result = {"article": article, "steps": steps, "relations": related}\n manual_cache.set(cache_key, result)\n return result', text ) # Invalidation: mutations = ['async def create_manual_article', 'async def update_manual_article', 'async def delete_manual_article'] for m in mutations: text = text.replace(m, "@manual_cache.clear\n" + m) # Actually, the python decorator is not made in cache.py, let me just add manual_cache.clear() inside them instead. text = text.replace('@manual_cache.clear\n', '') text = text.replace('return {"status": "success"', 'manual_cache.clear()\n return {"status": "success"') text = text.replace('return {"status": "created"', 'manual_cache.clear()\n return {"status": "created"') with open("app/modules/manual/backend/router.py", "w") as f: f.write(text)