grid-gent / gridgent /agents /narrator.py
James Afful
Add Grid-Gent Space code and Dockerfile (no binary assets)
458fa79
from __future__ import annotations
from typing import Dict, Any, List
class NarratorAgent:
def narrate(self, query: str, technical: Dict[str, Any]) -> str:
intent = technical.get("intent", "simulation")
if intent == "unknown":
msg = technical.get(
"message",
"I couldn't recognize a specific grid scenario in your question.",
)
lines: List[str] = []
lines.append(f"You asked: {query.strip()}")
lines.append("")
lines.append("I didn't see enough detail to run a grid scenario.")
lines.append(msg)
lines.append("")
lines.append("Try asking something like:")
lines.append("- What happens on feeder F2 if we add 5 MW of rooftop PV?")
lines.append("- Simulate adding 3 MW of load on feeder F1.")
lines.append("- What is the impact on voltages if load grows by 2 MW on feeder F3?")
return "\n".join(lines)
if intent == "explanation" and "power_flow" not in technical:
topic = technical.get("topic_hint", "").lower()
lines = []
lines.append(f"You asked: {query.strip()}")
lines.append("")
lines.append("This looks like a high-level explanation request rather than a specific feeder scenario.")
lines.append(
"Grid-Gent is a demo of an agentic assistant for distribution grids. It can explore simplified "
"scenarios like adding MW of load or PV on a feeder and report approximate loading and voltage impacts."
)
if "hosting" in topic or "capacity" in topic:
lines.append("")
lines.append(
"In real systems, 'hosting capacity' refers to how much additional DER (like rooftop PV) can be "
"connected without violating voltage, thermal, protection, or power quality limits. "
"Utilities usually study this using detailed feeder models and time-series simulations."
)
lines.append("")
lines.append("To see the demo in action, try asking things like:")
lines.append("- What happens on feeder F2 if we add 5 MW of rooftop PV?")
lines.append("- Simulate adding 3 MW of load on feeder F1.")
return "\n".join(lines)
pf = technical["power_flow"]
meta = technical["feeder_meta"]
loading_margin = float(technical.get("loading_margin_pct", 0.0))
lines: List[str] = []
lines.append(f"You asked: {query.strip()}")
lines.append("")
lines.append(f"Here's what Grid-Gent found for {meta['name']}:")
lines.append(
f"- Base peak demand (demo data): {meta['peak_mw']} MW with about {meta['num_customers']} customers."
)
lines.append(
f"- In this scenario, we assumed +{technical['added_load_mw']:.1f} MW of extra load and "
f"+{technical['added_pv_mw']:.1f} MW of additional PV."
)
lines.append("")
lines.append("Simplified power-flow-style results (demo model):")
lines.append(f"- Peak loading: {pf['peak_loading_pct']:.1f}% of an approximate rating.")
lines.append(f"- Loading margin to 100% (approx): {loading_margin:.1f}% points.")
lines.append(f"- Minimum voltage: {pf['min_voltage_pu']:.3f} pu")
lines.append(f"- Maximum voltage: {pf['max_voltage_pu']:.3f} pu")
if pf["overload_elements"]:
lines.append("")
lines.append("Potential issues flagged in this simplified view:")
for item in pf["overload_elements"]:
lines.append(f" • {item}")
else:
lines.append("")
lines.append("No major issues were flagged in this simplified view.")
if intent == "hosting_capacity":
lines.append("")
lines.append(
"Hosting capacity interpretation (demo-only): In this toy model, we look at loading and "
"voltage margins as a proxy for how much additional PV the feeder might host. "
"Here, the approximate loading and voltage range suggest whether the added PV appears acceptable "
"in this simplified analysis. Real hosting capacity studies must use detailed feeder models, "
"time-series behavior, and utility planning criteria, and are typically performed by power "
"system engineers."
)
lines.append("")
lines.append(pf["notes"])
lines.append("")
lines.append(
"Important: This is a deliberately simplified demonstration model. A real deployment would "
"use your actual network model, load/DER data, and planning criteria, and would treat all outputs "
"as advisory, subject to engineer review and formal studies. Do not use this demo for operational "
"or investment decisions."
)
return "\n".join(lines)