|
10 | 10 | from fastapi.middleware.cors import CORSMiddleware |
11 | 11 | from pydantic import BaseModel |
12 | 12 |
|
| 13 | +from ..context_intent.extractor import IntentExtractor |
13 | 14 | from ..context_intent.schema import ( |
14 | 15 | ConversationMessage, |
15 | 16 | DeploymentRecommendation, |
@@ -115,6 +116,12 @@ class DeploymentStatusResponse(BaseModel): |
115 | 116 | recommendations: list[str] | None = None |
116 | 117 |
|
117 | 118 |
|
| 119 | +class ExtractRequest(BaseModel): |
| 120 | + """Request for intent extraction from natural language.""" |
| 121 | + |
| 122 | + text: str |
| 123 | + |
| 124 | + |
118 | 125 | # Health check endpoint |
119 | 126 | @app.get("/health") |
120 | 127 | async def health_check(): |
@@ -247,6 +254,53 @@ async def list_use_cases(): |
247 | 254 | raise HTTPException(status_code=500, detail=str(e)) from e |
248 | 255 |
|
249 | 256 |
|
| 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 | + |
250 | 304 | def _round_to_nearest(value: float, nearest: int = 5) -> int: |
251 | 305 | """Round a value to the nearest multiple of `nearest`.""" |
252 | 306 | return int(round(value / nearest) * nearest) |
|
0 commit comments