Files
breakpilot-compliance/ai-compliance-sdk/internal/api/handlers/iace_handler_parser.go
T
Benjamin Admin d4b7943d54 feat: IACE deterministic narrative parser + library extensions
Library Extensions:
- 15 new components (C121-C135): knee lever, hydraulic ram, lubrication
  system, extraction system, vibrating plate, die tooling, transfer system,
  hoist, chute, oil drip tray, pressure relief valve, die space, flywheel,
  bin changeover station, inspection scale
- 8 new tags: person_under_load, two_hand_control_required,
  thermal_accumulation, mechanical_transmission, oil_mist_risk,
  rapid_energy_release, gravity_suspended_load, bypass_risk
- 14 new patterns (HP045-HP058): ram drop, die space crushing, oil mist
  inhalation, hot workpiece burns, suspended load, transfer draw-in,
  ejection fall, accumulator pressure release, impact noise, flywheel
  residual energy, guard bypass, two-hand misoperation, oil leakage,
  ergonomic bin changeover

Deterministic Parser (NO LLM):
- keyword_dictionary.go: ~100 entries mapping DE/EN keywords to
  component IDs, energy source IDs, and tags
- narrative_parser.go: ParseNarrative() extracts components, energy
  sources, lifecycle phases, roles, tech specs, and context tags from
  free-text machine descriptions via keyword matching + regex
- Tech spec regex: extracts kN, V, °C, bar, kW, rpm values and
  derives energy sources + severity tags automatically
- iace_handler_parser.go: POST /projects/:id/parse-narrative endpoint
  chains parser → pattern engine → hazard suggestions

Test: Paste Kniehebelpresse description → should detect 10+ components,
15+ hazards, all deterministically without LLM.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-05 00:29:18 +02:00

100 lines
3.3 KiB
Go

package handlers
import (
"net/http"
"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 matched patterns
for _, mp := range matchOutput.MatchedPatterns {
for _, cat := range mp.GeneratedHazardCats {
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: cat,
PatternID: mp.ID,
PatternName: mp.NameDE,
Priority: mp.Priority,
})
}
}
c.JSON(http.StatusOK, resp)
}