d7b287889e
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
111 lines
3.6 KiB
Go
111 lines
3.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/breakpilot/ai-compliance-sdk/internal/iace"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// ParseNarrativeRequest is the request body for POST /projects/:id/parse-narrative.
|
|
type ParseNarrativeRequest struct {
|
|
NarrativeText string `json:"narrative_text" binding:"required"`
|
|
}
|
|
|
|
// ParseNarrativeResponse contains the deterministic parsing results.
|
|
type ParseNarrativeResponse struct {
|
|
Components []iace.ComponentMatch `json:"components"`
|
|
EnergySources []iace.EnergyMatch `json:"energy_sources"`
|
|
LifecyclePhases []string `json:"lifecycle_phases"`
|
|
Roles []string `json:"roles"`
|
|
Tags []string `json:"tags"`
|
|
TechSpecs []iace.TechSpec `json:"tech_specs"`
|
|
Confidence float64 `json:"confidence"`
|
|
// Pattern match results (from feeding parsed data into pattern engine)
|
|
MatchedPatterns int `json:"matched_patterns"`
|
|
SuggestedHazards []struct {
|
|
Category string `json:"category"`
|
|
PatternID string `json:"pattern_id"`
|
|
PatternName string `json:"pattern_name"`
|
|
Priority int `json:"priority"`
|
|
} `json:"suggested_hazards"`
|
|
}
|
|
|
|
// ParseNarrative handles POST /projects/:id/parse-narrative
|
|
// Deterministically extracts components, energy sources, lifecycle phases,
|
|
// roles, and hazard patterns from a free-text machine description.
|
|
// NO LLM required — pure keyword matching + pattern engine.
|
|
func (h *IACEHandler) ParseNarrative(c *gin.Context) {
|
|
var req ParseNarrativeRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "narrative_text is required"})
|
|
return
|
|
}
|
|
|
|
// 1. Parse narrative text deterministically
|
|
parseResult := iace.ParseNarrative(req.NarrativeText)
|
|
|
|
// 2. Feed parsed tags into pattern engine
|
|
// Collect all component IDs for tag resolution
|
|
var componentIDs []string
|
|
for _, comp := range parseResult.Components {
|
|
componentIDs = append(componentIDs, comp.LibraryID)
|
|
}
|
|
var energyIDs []string
|
|
for _, e := range parseResult.EnergySources {
|
|
energyIDs = append(energyIDs, e.SourceID)
|
|
}
|
|
|
|
// Run pattern matching via PatternEngine
|
|
engine := iace.NewPatternEngine()
|
|
matchInput := iace.MatchInput{
|
|
ComponentLibraryIDs: componentIDs,
|
|
EnergySourceIDs: energyIDs,
|
|
LifecyclePhases: parseResult.LifecyclePhases,
|
|
CustomTags: parseResult.CustomTags,
|
|
}
|
|
matchOutput := engine.Match(matchInput)
|
|
|
|
// 3. Build response
|
|
resp := ParseNarrativeResponse{
|
|
Components: parseResult.Components,
|
|
EnergySources: parseResult.EnergySources,
|
|
LifecyclePhases: parseResult.LifecyclePhases,
|
|
Roles: parseResult.Roles,
|
|
Tags: parseResult.CustomTags,
|
|
TechSpecs: parseResult.TechSpecs,
|
|
Confidence: parseResult.Confidence,
|
|
MatchedPatterns: len(matchOutput.MatchedPatterns),
|
|
}
|
|
|
|
// Add suggested hazards from pattern engine output
|
|
for _, h := range matchOutput.SuggestedHazards {
|
|
// Find the first source pattern for name/priority lookup
|
|
patternName := ""
|
|
priority := 50
|
|
if len(h.SourcePatterns) > 0 {
|
|
for _, mp := range matchOutput.MatchedPatterns {
|
|
if mp.PatternID == h.SourcePatterns[0] {
|
|
patternName = mp.PatternName
|
|
priority = mp.Priority
|
|
break
|
|
}
|
|
}
|
|
}
|
|
resp.SuggestedHazards = append(resp.SuggestedHazards, struct {
|
|
Category string `json:"category"`
|
|
PatternID string `json:"pattern_id"`
|
|
PatternName string `json:"pattern_name"`
|
|
Priority int `json:"priority"`
|
|
}{
|
|
Category: h.Category,
|
|
PatternID: strings.Join(h.SourcePatterns, ","),
|
|
PatternName: patternName,
|
|
Priority: priority,
|
|
})
|
|
}
|
|
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|