Skip to content

Commit 9d156ad

Browse files
committed
feat(nlp): Complete natural language E2E integration for network slicing
Implemented comprehensive natural language processing pipeline enabling end-to-end network slice deployment from conversational intents. ## NLP Service (FastAPI) - nlp/nlp_service.py: Production HTTP service wrapping intent parser - POST /api/v1/parse: Parse natural language to QoS parameters - GET /health: Service health monitoring - GET /api/v1/history: Intent processing history - Full error handling and validation - Test coverage: 9/9 tests passing ## Go NLP Client - orchestrator/pkg/nlp/client.go: Type-safe HTTP client - Health check and intent parsing APIs - Context-aware timeout handling - Test coverage: 4/4 tests passing ## Orchestrator Integration - POST /api/v1/intents/natural: Natural language endpoint - Complete flow: NL → NLP Service → QoS Mapping → Argo CD → K8s - Prometheus metrics integration - Session tracking support ## E2E Testing - orchestrator/test/e2e_natural_language_test.sh: Automated test suite - Tests all slice types (eMBB, URLLC, mMTC) - Kubernetes deployment verification - Performance metrics collection ## Architecture ``` Natural Language Input ↓ NLP Service (FastAPI:8082) → Intent Parser → QoS Mapping ↓ HTTP Go Orchestrator (8080) → NLP Client → Argo CD Client ↓ Argo CD Applications (ConfigMap-based) ↓ Kubernetes Resources (Namespace, Deployment, Service, ConfigMap) ``` ## Example Usage ```bash curl -X POST http://localhost:8080/api/v1/intents/natural \ -H "Content-Type: application/json" \ -d '{"intent": "Deploy high-bandwidth video streaming for 100 users"}' ``` ## Test Results - NLP Service: 9/9 tests ✓ - NLP Client: 4/4 tests ✓ - Argo CD Integration: 21/21 tests ✓ - Orchestrator compilation: Success ✓ This enables production-ready natural language network slice orchestration with full observability, error handling, and GitOps integration.
1 parent 6532137 commit 9d156ad

7 files changed

Lines changed: 1015 additions & 0 deletions

File tree

nlp/nlp_service.py

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
#!/usr/bin/env python3
2+
"""
3+
NLP Service for O-RAN Intent-Based MANO
4+
FastAPI HTTP service wrapping intent_parser.py
5+
Provides REST API for natural language intent processing
6+
"""
7+
8+
from fastapi import FastAPI, HTTPException
9+
from fastapi.middleware.cors import CORSMiddleware
10+
from pydantic import BaseModel, Field
11+
from typing import Dict, Any, Optional
12+
import logging
13+
import uvicorn
14+
from datetime import datetime
15+
16+
# Import the intent parser
17+
from intent_parser import IntentParser, IntentValidationError, QoSMapping
18+
19+
# Configure logging
20+
logging.basicConfig(
21+
level=logging.INFO,
22+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
23+
)
24+
logger = logging.getLogger(__name__)
25+
26+
# Create FastAPI app
27+
app = FastAPI(
28+
title="O-RAN NLP Service",
29+
description="Natural Language Processing service for O-RAN Intent-Based MANO",
30+
version="1.0.0",
31+
docs_url="/api/docs",
32+
redoc_url="/api/redoc"
33+
)
34+
35+
# Add CORS middleware
36+
app.add_middleware(
37+
CORSMiddleware,
38+
allow_origins=["*"], # In production, specify exact origins
39+
allow_credentials=True,
40+
allow_methods=["*"],
41+
allow_headers=["*"],
42+
)
43+
44+
# Global intent parser instance
45+
parser = IntentParser()
46+
47+
# Pydantic models for API
48+
class IntentRequest(BaseModel):
49+
"""Request model for intent parsing"""
50+
intent: str = Field(..., min_length=1, max_length=1000, description="Natural language intent")
51+
session_id: Optional[str] = Field(None, description="Optional session ID for tracking")
52+
53+
class Config:
54+
schema_extra = {
55+
"example": {
56+
"intent": "Deploy high-bandwidth video streaming slice for 100 concurrent users",
57+
"session_id": "session-20250101-001"
58+
}
59+
}
60+
61+
class QoSResponse(BaseModel):
62+
"""Response model for parsed QoS parameters"""
63+
slice_type: str
64+
throughput_mbps: float
65+
latency_ms: float
66+
packet_loss_rate: float
67+
priority: int
68+
jitter_ms: Optional[float] = None
69+
bandwidth_guarantee: Optional[float] = None
70+
reliability: Optional[float] = None
71+
72+
class IntentResponse(BaseModel):
73+
"""Response model for intent parsing"""
74+
success: bool
75+
slice_type: str
76+
qos_profile: QoSResponse
77+
raw_intent: str
78+
session_id: Optional[str] = None
79+
timestamp: str
80+
processing_time_ms: float
81+
82+
class Config:
83+
schema_extra = {
84+
"example": {
85+
"success": True,
86+
"slice_type": "eMBB",
87+
"qos_profile": {
88+
"slice_type": "eMBB",
89+
"throughput_mbps": 50.0,
90+
"latency_ms": 10.0,
91+
"packet_loss_rate": 0.001,
92+
"priority": 5,
93+
"reliability": 0.999
94+
},
95+
"raw_intent": "Deploy high-bandwidth video streaming slice",
96+
"session_id": "session-20250101-001",
97+
"timestamp": "2025-10-01T00:45:00Z",
98+
"processing_time_ms": 12.5
99+
}
100+
}
101+
102+
class ErrorResponse(BaseModel):
103+
"""Error response model"""
104+
success: bool = False
105+
error: str
106+
detail: Optional[str] = None
107+
timestamp: str
108+
109+
class HealthResponse(BaseModel):
110+
"""Health check response"""
111+
status: str
112+
version: str
113+
uptime_seconds: float
114+
total_intents_processed: int
115+
116+
# Metrics
117+
start_time = datetime.now()
118+
total_intents = 0
119+
120+
@app.get("/health", response_model=HealthResponse)
121+
async def health_check():
122+
"""Health check endpoint"""
123+
uptime = (datetime.now() - start_time).total_seconds()
124+
return HealthResponse(
125+
status="healthy",
126+
version="1.0.0",
127+
uptime_seconds=uptime,
128+
total_intents_processed=total_intents
129+
)
130+
131+
@app.post("/api/v1/parse", response_model=IntentResponse)
132+
async def parse_intent(request: IntentRequest):
133+
"""
134+
Parse natural language intent into QoS parameters
135+
136+
Args:
137+
request: IntentRequest with natural language text
138+
139+
Returns:
140+
IntentResponse with parsed QoS parameters
141+
142+
Raises:
143+
HTTPException: If parsing fails
144+
"""
145+
global total_intents
146+
147+
start = datetime.now()
148+
logger.info(f"Parsing intent: {request.intent[:50]}...")
149+
150+
try:
151+
# Parse the intent
152+
mapping: QoSMapping = parser.parse(request.intent)
153+
154+
# Create QoS response
155+
qos_response = QoSResponse(
156+
slice_type=mapping.slice_type.value,
157+
throughput_mbps=mapping.throughput_mbps,
158+
latency_ms=mapping.latency_ms,
159+
packet_loss_rate=mapping.packet_loss_rate,
160+
priority=mapping.priority,
161+
jitter_ms=mapping.jitter_ms,
162+
bandwidth_guarantee=mapping.bandwidth_guarantee,
163+
reliability=mapping.reliability
164+
)
165+
166+
# Calculate processing time
167+
processing_time = (datetime.now() - start).total_seconds() * 1000
168+
169+
# Update metrics
170+
total_intents += 1
171+
172+
logger.info(f"✓ Parsed as {mapping.slice_type.value} in {processing_time:.2f}ms")
173+
174+
return IntentResponse(
175+
success=True,
176+
slice_type=mapping.slice_type.value,
177+
qos_profile=qos_response,
178+
raw_intent=request.intent,
179+
session_id=request.session_id,
180+
timestamp=datetime.now().isoformat(),
181+
processing_time_ms=processing_time
182+
)
183+
184+
except IntentValidationError as e:
185+
logger.error(f"Validation error: {e}")
186+
raise HTTPException(
187+
status_code=400,
188+
detail={
189+
"success": False,
190+
"error": "Intent validation failed",
191+
"detail": str(e),
192+
"timestamp": datetime.now().isoformat()
193+
}
194+
)
195+
except ValueError as e:
196+
logger.error(f"Value error: {e}")
197+
raise HTTPException(
198+
status_code=400,
199+
detail={
200+
"success": False,
201+
"error": "Invalid intent format",
202+
"detail": str(e),
203+
"timestamp": datetime.now().isoformat()
204+
}
205+
)
206+
except Exception as e:
207+
logger.error(f"Unexpected error: {e}", exc_info=True)
208+
raise HTTPException(
209+
status_code=500,
210+
detail={
211+
"success": False,
212+
"error": "Internal server error",
213+
"detail": str(e),
214+
"timestamp": datetime.now().isoformat()
215+
}
216+
)
217+
218+
@app.get("/api/v1/history")
219+
async def get_history():
220+
"""Get history of parsed intents"""
221+
history = parser.get_history()
222+
return {
223+
"success": True,
224+
"count": len(history),
225+
"intents": [
226+
{
227+
"intent": intent,
228+
"slice_type": mapping.slice_type.value,
229+
"throughput_mbps": mapping.throughput_mbps,
230+
"latency_ms": mapping.latency_ms
231+
}
232+
for intent, mapping in history
233+
]
234+
}
235+
236+
@app.get("/")
237+
async def root():
238+
"""Root endpoint"""
239+
return {
240+
"service": "O-RAN NLP Service",
241+
"version": "1.0.0",
242+
"status": "running",
243+
"docs": "/api/docs"
244+
}
245+
246+
if __name__ == "__main__":
247+
logger.info("🚀 Starting O-RAN NLP Service")
248+
logger.info("📡 API Documentation: http://localhost:8082/api/docs")
249+
logger.info("🔍 Health Check: http://localhost:8082/health")
250+
251+
uvicorn.run(
252+
app,
253+
host="0.0.0.0",
254+
port=8082,
255+
log_level="info"
256+
)

nlp/requirements-nlp-service.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# NLP Service Dependencies
2+
fastapi==0.115.5
3+
uvicorn[standard]==0.34.0
4+
pydantic==2.10.3
5+
python-multipart==0.0.20

nlp/test_nlp_service.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Tests for NLP Service API
4+
"""
5+
6+
import pytest
7+
from fastapi.testclient import TestClient
8+
from nlp_service import app
9+
10+
client = TestClient(app)
11+
12+
def test_health_check():
13+
"""Test health check endpoint"""
14+
response = client.get("/health")
15+
assert response.status_code == 200
16+
data = response.json()
17+
assert data["status"] == "healthy"
18+
assert "version" in data
19+
assert "uptime_seconds" in data
20+
21+
def test_root_endpoint():
22+
"""Test root endpoint"""
23+
response = client.get("/")
24+
assert response.status_code == 200
25+
data = response.json()
26+
assert data["service"] == "O-RAN NLP Service"
27+
assert data["status"] == "running"
28+
29+
def test_parse_embb_intent():
30+
"""Test parsing eMBB intent"""
31+
response = client.post(
32+
"/api/v1/parse",
33+
json={
34+
"intent": "Deploy high-bandwidth video streaming slice for 100 users",
35+
"session_id": "test-session-001"
36+
}
37+
)
38+
assert response.status_code == 200
39+
data = response.json()
40+
assert data["success"] is True
41+
assert data["slice_type"] == "eMBB"
42+
assert data["qos_profile"]["throughput_mbps"] == 50.0
43+
assert data["qos_profile"]["latency_ms"] == 10.0
44+
assert data["session_id"] == "test-session-001"
45+
46+
def test_parse_urllc_intent():
47+
"""Test parsing URLLC intent"""
48+
response = client.post(
49+
"/api/v1/parse",
50+
json={
51+
"intent": "Deploy ultra-low latency slice for autonomous vehicles"
52+
}
53+
)
54+
assert response.status_code == 200
55+
data = response.json()
56+
assert data["success"] is True
57+
assert data["slice_type"] == "URLLC"
58+
assert data["qos_profile"]["latency_ms"] == 1.0
59+
assert data["qos_profile"]["reliability"] == 0.99999
60+
61+
def test_parse_mmtc_intent():
62+
"""Test parsing mMTC intent"""
63+
response = client.post(
64+
"/api/v1/parse",
65+
json={
66+
"intent": "Deploy IoT sensor network for smart city monitoring"
67+
}
68+
)
69+
assert response.status_code == 200
70+
data = response.json()
71+
assert data["success"] is True
72+
assert data["slice_type"] == "mMTC"
73+
74+
def test_empty_intent():
75+
"""Test empty intent validation"""
76+
response = client.post(
77+
"/api/v1/parse",
78+
json={"intent": ""}
79+
)
80+
# FastAPI/Pydantic returns 422 for validation errors
81+
assert response.status_code == 422
82+
83+
def test_invalid_intent():
84+
"""Test invalid intent (only numbers)"""
85+
response = client.post(
86+
"/api/v1/parse",
87+
json={"intent": "12345 !@#$%"}
88+
)
89+
assert response.status_code == 400
90+
91+
def test_get_history():
92+
"""Test history endpoint"""
93+
# First parse an intent
94+
client.post(
95+
"/api/v1/parse",
96+
json={"intent": "Deploy video streaming slice"}
97+
)
98+
99+
# Get history
100+
response = client.get("/api/v1/history")
101+
assert response.status_code == 200
102+
data = response.json()
103+
assert data["success"] is True
104+
assert "intents" in data
105+
assert len(data["intents"]) > 0
106+
107+
def test_processing_time():
108+
"""Test that processing time is tracked"""
109+
response = client.post(
110+
"/api/v1/parse",
111+
json={"intent": "Deploy high-speed data transfer slice"}
112+
)
113+
assert response.status_code == 200
114+
data = response.json()
115+
assert "processing_time_ms" in data
116+
assert data["processing_time_ms"] > 0
117+
118+
if __name__ == "__main__":
119+
pytest.main([__file__, "-v"])

0 commit comments

Comments
 (0)