Skip to content

Commit 8dcbb6d

Browse files
committed
refactor(singleton): wire OmniParser, RAG KB, and engine via app.state
1 parent 3b7da1a commit 8dcbb6d

4 files changed

Lines changed: 39 additions & 44 deletions

File tree

app/api/routes/evaluation.py

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import logging
22
from fastapi import APIRouter, HTTPException, UploadFile, File, Body, Request
3-
from typing import Optional, List
4-
import json
3+
from typing import List
54

6-
from app.services.heuristic_engine import HeuristicEvaluationEngine
7-
from app.core.config import settings, ALLOWED_IMAGE_TYPES
8-
from app.services.exceptions import InvalidInputError
5+
from app.core.config import ALLOWED_IMAGE_TYPES
6+
from app.services.exceptions import InvalidInputError, ModelInferenceError, RAGKnowledgeBaseError
7+
from app.services.omniparser_client import UIElement
8+
from app.core.constants import HeuristicId, NIELSEN_HEURISTICS
99

1010
logger = logging.getLogger(__name__)
1111
router = APIRouter()
@@ -55,12 +55,13 @@ async def evaluate_heuristics(
5555
detection_client = request.app.state.omniparser_client
5656
contents = await image.read()
5757

58-
detection_result = await detection_client.detect_elements(contents)
59-
60-
# Initialize evaluation engine and evaluate
61-
evaluation_engine = HeuristicEvaluationEngine()
62-
await evaluation_engine.initialize()
58+
detection_result = await detection_client.detect_elements(
59+
contents,
60+
content_type=content_type
61+
)
6362

63+
# Use singleton evaluation engine and evaluate
64+
evaluation_engine = request.app.state.heuristic_engine
6465
evaluation_result = await evaluation_engine.evaluate_interface(detection_result)
6566

6667
return {
@@ -82,6 +83,7 @@ async def evaluate_heuristics(
8283

8384
@router.post("/evaluate-legacy/{heuristic_id}")
8485
async def evaluate_legacy_format(
86+
request: Request,
8587
heuristic_id: str,
8688
elements: List[dict] = Body(...)
8789
):
@@ -100,7 +102,6 @@ async def evaluate_legacy_format(
100102
500: Unexpected server error
101103
"""
102104
try:
103-
from app.core.constants import HeuristicId
104105

105106
# Normalize heuristic_id to uppercase
106107
normalized_id = heuristic_id.upper()
@@ -113,10 +114,8 @@ async def evaluate_legacy_format(
113114
}
114115
)
115116

116-
evaluation_engine = HeuristicEvaluationEngine()
117-
await evaluation_engine.initialize()
117+
evaluation_engine = request.app.state.heuristic_engine
118118

119-
from app.services.omniparser_client import UIElement
120119
try:
121120
ui_elements = [UIElement.from_dict(e) for e in elements]
122121
except Exception as e:
@@ -168,8 +167,6 @@ async def evaluate_legacy_format(
168167

169168
@router.get("/heuristics")
170169
async def get_heuristics():
171-
from app.core.constants import NIELSEN_HEURISTICS
172-
173170
return {
174171
"success": True,
175172
"data": {
@@ -183,7 +180,7 @@ async def get_heuristics():
183180
}
184181

185182
@router.get("/knowledge-base/stats")
186-
async def get_knowledge_base_stats():
183+
async def get_knowledge_base_stats(request: Request):
187184
"""Get statistics about the RAG knowledge base.
188185
189186
Returns:
@@ -194,10 +191,7 @@ async def get_knowledge_base_stats():
194191
500: Unexpected server error
195192
"""
196193
try:
197-
from app.services.rag_knowledge_base import RAGKnowledgeBase
198-
199-
kb = RAGKnowledgeBase()
200-
await kb.initialize()
194+
kb = request.app.state.rag_knowledge_base
201195
stats = await kb.get_stats()
202196

203197
return {

app/api/routes/heuristic.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import logging
22
from fastapi import APIRouter, HTTPException, UploadFile, File, Form, Request
33
from typing import Optional
4-
from PIL import Image
5-
import io
64

7-
from app.services.omniparser_client import OmniParserClient, UIElementDetectionResult
85

96
logger = logging.getLogger(__name__)
107
router = APIRouter()
@@ -37,11 +34,11 @@ async def detect_ui_elements(
3734

3835
@router.post("/analyze")
3936
async def analyze_interface(
37+
request: Request,
4038
image: UploadFile = File(...)
4139
):
4240
try:
43-
client = OmniParserClient()
44-
await client.initialize()
41+
client = request.app.state.omniparser_client
4542

4643
contents = await image.read()
4744
result = await client.detect_elements(contents)

app/services/heuristic_engine.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
import json
33
from typing import List, Dict, Any, Optional
44
from datetime import datetime
5-
import asyncio
65
from openai import AsyncOpenAI
76

87
from app.core.constants import NIELSEN_HEURISTICS, HeuristicId, SeverityLevel
98
from app.core.config import settings
109
from app.services.omniparser_client import UIElementDetectionResult, UIElement
1110
from app.services.rag_knowledge_base import RAGKnowledgeBase
12-
from app.services.exceptions import ModelInferenceError, InvalidInputError
11+
from app.services.exceptions import ModelInferenceError
1312

1413
logger = logging.getLogger(__name__)
1514

@@ -111,24 +110,28 @@ class HeuristicEvaluationEngine:
111110
Evaluation Method: LLM-based with prompt engineering
112111
113112
Example Usage:
114-
engine = HeuristicEvaluationEngine()
113+
engine = request.app.state.heuristic_engine
115114
result = await engine.evaluate_interface(detection_result)
116115
"""
117116

118-
def __init__(self):
117+
def __init__(self, rag_kb: Optional[RAGKnowledgeBase] = None):
119118
self.logger = logging.getLogger(__name__)
120119
self.llm_client = None
121-
self.rag_kb = None
120+
self.rag_kb = rag_kb
122121
self.initialized = False
123122

124123
async def initialize(self):
124+
if self.initialized:
125+
return
125126
self.logger.info("Initializing Heuristic Evaluation Engine...")
126127
self.llm_client = AsyncOpenAI(
127128
api_key=settings.OPENAI_API_KEY,
128129
base_url=settings.OPENAI_BASE_URL
129130
)
130-
self.rag_kb = RAGKnowledgeBase()
131-
await self.rag_kb.initialize()
131+
if self.rag_kb is None:
132+
raise RuntimeError("RAGKnowledgeBase dependency missing. Inject via startup.")
133+
if not self.rag_kb.index_initialized:
134+
await self.rag_kb.initialize()
132135
self.initialized = True
133136
self.logger.info("Heuristic Evaluation Engine initialized")
134137

main.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
from fastapi import FastAPI, HTTPException, UploadFile, File
1+
from fastapi import FastAPI
22
from fastapi.middleware.cors import CORSMiddleware
3-
from fastapi.responses import JSONResponse
43
import logging
5-
import os
6-
from pathlib import Path
74

85
from app.core.config import settings
96
from app.services.heuristic_engine import HeuristicEvaluationEngine
@@ -32,24 +29,28 @@
3229

3330
@app.on_event("startup")
3431
async def startup_event():
35-
# Initialize OmniParser Client (Singleton)
36-
app.state.omniparser_client = OmniParserClient()
37-
await app.state.omniparser_client.initialize()
38-
3932
setup_logging()
4033
logger = logging.getLogger(__name__)
4134
logger.info("AI Heuristic Evaluation API starting up...")
4235

43-
# Initialize singleton OmniParser client to avoid re-initializing model on every request
36+
# Initialize OmniParser Client (Singleton)
4437
app.state.omniparser_client = OmniParserClient()
4538
await app.state.omniparser_client.initialize()
4639
logger.info("OmniParser client initialized (singleton)")
4740

48-
heuristic_engine = HeuristicEvaluationEngine()
49-
await heuristic_engine.initialize()
41+
# Initialize RAG Knowledge Base (Singleton)
42+
app.state.rag_knowledge_base = RAGKnowledgeBase()
43+
await app.state.rag_knowledge_base.initialize()
44+
logger.info("RAG knowledge base initialized")
5045

46+
# Initilize Hueristic Evaluation Engine (Singleton)
47+
app.state.heuristic_engine = HeuristicEvaluationEngine(
48+
rag_kb=app.state.rag_knowledge_base
49+
)
50+
await app.state.heuristic_engine.initialize()
5151
logger.info("Heuristic evaluation engine initialized")
5252

53+
5354
@app.on_event("shutdown")
5455
async def shutdown_event():
5556
logger = logging.getLogger(__name__)

0 commit comments

Comments
 (0)