Skip to content

Commit e42e779

Browse files
committed
feat: Add /api/v1/extract endpoint for LLM-based intent extraction
Add POST /api/v1/extract endpoint that uses IntentExtractor to parse natural language deployment requirements via Ollama (Qwen 2.5 7B). This endpoint had gone missing, so the code was always falling backto a mock extraction call. Fixes: #52 Signed-off-by: Andre Fredette <afredette@redhat.com>
1 parent 3cee8e5 commit e42e779

File tree

1 file changed

+54
-0
lines changed

1 file changed

+54
-0
lines changed

backend/src/api/routes.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from fastapi.middleware.cors import CORSMiddleware
1111
from pydantic import BaseModel
1212

13+
from ..context_intent.extractor import IntentExtractor
1314
from ..context_intent.schema import (
1415
ConversationMessage,
1516
DeploymentRecommendation,
@@ -115,6 +116,12 @@ class DeploymentStatusResponse(BaseModel):
115116
recommendations: list[str] | None = None
116117

117118

119+
class ExtractRequest(BaseModel):
120+
"""Request for intent extraction from natural language."""
121+
122+
text: str
123+
124+
118125
# Health check endpoint
119126
@app.get("/health")
120127
async def health_check():
@@ -247,6 +254,53 @@ async def list_use_cases():
247254
raise HTTPException(status_code=500, detail=str(e)) from e
248255

249256

257+
@app.post("/api/v1/extract")
258+
async def extract_intent(request: ExtractRequest):
259+
"""Extract business context from natural language using LLM.
260+
261+
Takes a user's natural language description of their deployment needs
262+
and extracts structured intent using Ollama (Qwen 2.5 7B).
263+
264+
Args:
265+
request: ExtractRequest with 'text' field containing user input
266+
267+
Returns:
268+
Structured intent with use_case, user_count, priority, etc.
269+
"""
270+
logger.info("=" * 60)
271+
logger.info("EXTRACT INTENT REQUEST")
272+
logger.info("=" * 60)
273+
logger.info(f" Input text: {request.text[:200]}{'...' if len(request.text) > 200 else ''}")
274+
275+
try:
276+
# Create intent extractor (uses workflow's LLM client)
277+
intent_extractor = IntentExtractor(workflow.llm_client)
278+
279+
# Extract intent from natural language
280+
intent = intent_extractor.extract_intent(request.text)
281+
282+
# Infer any missing fields based on use case
283+
intent = intent_extractor.infer_missing_fields(intent)
284+
285+
logger.info(f" Extracted use_case: {intent.use_case}")
286+
logger.info(f" Extracted user_count: {intent.user_count}")
287+
logger.info(f" Extracted priority: {intent.latency_requirement}")
288+
logger.info("=" * 60)
289+
290+
# Return as dict for JSON serialization
291+
# Map latency_requirement to 'priority' for UI compatibility
292+
result = intent.model_dump()
293+
result["priority"] = intent.latency_requirement
294+
return result
295+
296+
except ValueError as e:
297+
logger.error(f"Intent extraction failed: {e}")
298+
raise HTTPException(status_code=422, detail=str(e)) from e
299+
except Exception as e:
300+
logger.error(f"Unexpected error during intent extraction: {e}")
301+
raise HTTPException(status_code=500, detail=str(e)) from e
302+
303+
250304
def _round_to_nearest(value: float, nearest: int = 5) -> int:
251305
"""Round a value to the nearest multiple of `nearest`."""
252306
return int(round(value / nearest) * nearest)

0 commit comments

Comments
 (0)