Initial commit: CMM Report Analyzer
FastAPI app that parses CMM inspection reports (PDF/Excel/CSV), computes SPC metrics (Cp/Cpk/Pp/Ppk, control limits, Shapiro-Wilk), generates interactive Plotly charts, and provides AI-powered quality summaries via Azure OpenAI with graceful fallback. Includes 21 passing tests covering parsers, SPC calculations, and API endpoints.
This commit is contained in:
101
app/ai/summarizer.py
Normal file
101
app/ai/summarizer.py
Normal file
@@ -0,0 +1,101 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from openai import AsyncAzureOpenAI
|
||||
|
||||
from app.analysis.spc import SPCResult
|
||||
from app.config import settings
|
||||
from app.parsers.models import ParsedReport
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
SYSTEM_PROMPT = """\
|
||||
You are a quality engineer reviewing CMM (Coordinate Measuring Machine) inspection data.
|
||||
Provide a concise, actionable summary that includes:
|
||||
1. Overall pass/fail assessment
|
||||
2. Features of concern (low Cpk, out-of-tolerance, trends)
|
||||
3. Root-cause hypotheses for any deviations
|
||||
4. Recommended corrective actions
|
||||
Use precise engineering language. Reference feature names and numeric values."""
|
||||
|
||||
|
||||
async def summarize(
|
||||
report: ParsedReport, spc_results: list[SPCResult]
|
||||
) -> str:
|
||||
"""Generate an AI-powered quality summary. Returns fallback text on failure."""
|
||||
if not settings.azure_openai_endpoint or not settings.azure_openai_api_key:
|
||||
return _fallback_summary(report, spc_results)
|
||||
|
||||
spc_text = _format_spc(spc_results)
|
||||
user_msg = (
|
||||
f"File: {report.filename}\n"
|
||||
f"Measurements: {len(report.measurements)}, "
|
||||
f"Out of tolerance: {len(report.out_of_tolerance)}\n\n"
|
||||
f"SPC Results:\n{spc_text}\n\n"
|
||||
f"Raw report excerpt:\n{report.raw_text[:3000]}"
|
||||
)
|
||||
|
||||
try:
|
||||
client = AsyncAzureOpenAI(
|
||||
azure_endpoint=settings.azure_openai_endpoint,
|
||||
api_key=settings.azure_openai_api_key,
|
||||
api_version=settings.azure_openai_api_version,
|
||||
)
|
||||
response = await client.chat.completions.create(
|
||||
model=settings.azure_openai_deployment,
|
||||
messages=[
|
||||
{"role": "system", "content": SYSTEM_PROMPT},
|
||||
{"role": "user", "content": user_msg},
|
||||
],
|
||||
temperature=0.3,
|
||||
max_tokens=1024,
|
||||
)
|
||||
return response.choices[0].message.content or _fallback_summary(
|
||||
report, spc_results
|
||||
)
|
||||
except Exception:
|
||||
logger.exception("Azure OpenAI call failed, using fallback summary")
|
||||
return _fallback_summary(report, spc_results)
|
||||
|
||||
|
||||
def _format_spc(results: list[SPCResult]) -> str:
|
||||
lines: list[str] = []
|
||||
for r in results:
|
||||
cpk_str = f"{r.cpk:.3f}" if r.cpk is not None else "N/A"
|
||||
ppk_str = f"{r.ppk:.3f}" if r.ppk is not None else "N/A"
|
||||
lines.append(
|
||||
f" {r.feature_name}: n={r.n}, mean={r.mean:.4f}, "
|
||||
f"Cpk={cpk_str}, Ppk={ppk_str}, OOS={r.out_of_spec_count}"
|
||||
)
|
||||
return "\n".join(lines) if lines else " No SPC data available."
|
||||
|
||||
|
||||
def _fallback_summary(report: ParsedReport, spc_results: list[SPCResult]) -> str:
|
||||
total = len(report.measurements)
|
||||
oot = len(report.out_of_tolerance)
|
||||
status = "PASS" if oot == 0 else "FAIL"
|
||||
|
||||
lines = [
|
||||
f"**Inspection Summary for {report.filename}**",
|
||||
f"Status: **{status}** — {total} measurements, {oot} out of tolerance.",
|
||||
"",
|
||||
]
|
||||
if oot > 0:
|
||||
lines.append("Out-of-tolerance features:")
|
||||
for m in report.out_of_tolerance:
|
||||
lines.append(
|
||||
f" - {m.feature_name}: actual={m.actual:.4f}, "
|
||||
f"nominal={m.nominal:.4f}, tolerance=[{m.lsl:.4f}, {m.usl:.4f}]"
|
||||
)
|
||||
lines.append("")
|
||||
|
||||
for r in spc_results:
|
||||
if r.cpk is not None and r.cpk < 1.0:
|
||||
lines.append(
|
||||
f" Warning: {r.feature_name} Cpk={r.cpk:.3f} (below 1.0)"
|
||||
)
|
||||
|
||||
lines.append("")
|
||||
lines.append("*(AI summary unavailable — configure Azure OpenAI for enhanced analysis)*")
|
||||
return "\n".join(lines)
|
||||
Reference in New Issue
Block a user