import sys import os import time import asyncio from unittest.mock import patch, MagicMock sys.path.append(os.path.dirname(os.path.abspath(__file__))) from endpoints.projects import autofix_project_section from fastapi import HTTPException # Mock klas class DummyProject: id = "proj_1" clerk_user_id = "clerk_123" title = "Projekt Testowy" program_type = "SMART" final_document_audit_result = { "issues": [ { "affected_section": "Ogólne", "severity": "high", "category": "logic", "message": "Brak spójności", "recommendation": "Popraw spójność", } ] } external_context = {} class DummySection: id = "sec_1" project_id = "proj_1" section_type = "project_summary" content = "Oryginalna treść sekcji." class DummyQuery: def __init__(self, model): self.model = model def filter(self, *args, **kwargs): return self def join(self, *args, **kwargs): return self def first(self): if hasattr(self.model, "__name__"): if self.model.__name__ == "Project": return DummyProject() elif self.model.__name__ == "ProjectSection": return DummySection() return None def all(self): return [] class DummyDB: def query(self, model, *args, **kwargs): return DummyQuery(model) def add(self, obj): pass def commit(self): pass def refresh(self, obj): pass def run_simulation(): print("==================================================") print("[SYMULACJA] BŁĘDÓW 429 (RATE LIMIT) LLM W AUTOFIX") print("==================================================") db = DummyDB() call_count = 0 # Mock dla metody invoke w łańcuchu LangChain def mock_chain_invoke(*args, **kwargs): nonlocal call_count call_count += 1 print(f"\n[LLM Request] Próba nr {call_count}...") if call_count < 3: print( "[LLM Error] Symulowanie bledu: 429 ResourceExhausted (Przekroczono limit Quota)" ) # Rzucamy wyjątek, który backend normalnie by dostał od Google API raise Exception("429 ResourceExhausted: Quota exceeded for AI") print("[LLM Success] Zwracanie poprawnej odpowiedzi na 3. probie!") mock_response = MagicMock() mock_response.content = ( "Zaktualizowana i poprawiona treść sekcji z uwzględnieniem audytu." ) return mock_response # Ponieważ 'chain' jest tworzony wewnątrz funkcji, zrobimy patch na PromptTemplate.__or__ # albo bezpośrednio na klasie RunnableSequence with patch("endpoints.projects.get_llm") as mock_get_llm: # Konstruujemy mockowany LLM mock_llm = MagicMock() mock_get_llm.return_value = mock_llm # Zamiast patchować '|' (or), po prostu zrobimy patch na chain.invoke # w Pythonie możemy użyć mocka na całej klasie PromptTemplate, ale łatwiej patchować llm # Mockujemy zachowanie łańcucha prompt | llm # W Langchain operator | tworzy RunnableSequence. Mockujemy zachowanie invoke na wyniku with patch( "endpoints.projects.PromptTemplate.from_template" ) as mock_from_template: mock_prompt = MagicMock() mock_from_template.return_value = mock_prompt mock_chain = MagicMock() mock_chain.invoke.side_effect = mock_chain_invoke mock_prompt.__or__.return_value = mock_chain # Należy też zmockować ProjectSectionVersion with patch("endpoints.projects.ProjectSectionVersion"): try: start_time = time.time() # Nowa sygnatura funkcji result = asyncio.run( autofix_project_section( project_id="proj_1", section_id="sec_1", token_data={"sub": "clerk_123"}, db=db, ) ) end_time = time.time() print("\n==================================================") print("[WYNIK SYMULACJI]") print("Status: SUKCES (Udało się przetrwać błędy 429!)") print( f"Całkowity czas wykonania: {end_time - start_time:.2f} sekund" ) print(f"Zwrócony tekst:\n{result.content}") print("==================================================") except HTTPException as e: print( f"\n[Frontend Error] Funkcja rzucila HTTP 429 do Frontendu po wyczerpaniu limitu prob: {e.detail}" ) if __name__ == "__main__": run_simulation()