Saturday, April 4, 2026

orchestrator_agent.py

from google.adk.agents import Agent

# ── Mock Parts Inventory ──────────────────────────────────────────────────────
PARTS_INVENTORY = {
    "Brake pads x4": {
        "depot_a_manchester": 12,
        "depot_b_birmingham": 4,
        "depot_c_leeds": 0,
        "unit_cost_gbp": 140,
        "lead_time_days": 0,
    },
    "Brake adjustment kit": {
        "depot_a_manchester": 8,
        "depot_b_birmingham": 0,
        "depot_c_leeds": 3,
        "unit_cost_gbp": 35,
        "lead_time_days": 0,
    },
    "Refrigerant R-404A": {
        "depot_a_manchester": 2,
        "depot_b_birmingham": 0,
        "depot_c_leeds": 0,
        "unit_cost_gbp": 320,
        "lead_time_days": 1,
    },
    "Compressor filter kit": {
        "depot_a_manchester": 0,
        "depot_b_birmingham": 1,
        "depot_c_leeds": 0,
        "unit_cost_gbp": 85,
        "lead_time_days": 2,
    },
    "Nitrogen inflation kit": {
        "depot_a_manchester": 5,
        "depot_b_birmingham": 5,
        "depot_c_leeds": 2,
        "unit_cost_gbp": 20,
        "lead_time_days": 0,
    },
    "Tyre pressure gauge": {
        "depot_a_manchester": 10,
        "depot_b_birmingham": 8,
        "depot_c_leeds": 6,
        "unit_cost_gbp": 15,
        "lead_time_days": 0,
    },
}

# ── Mock Asset Database ───────────────────────────────────────────────────────
ASSET_DB = {
    "TRL-001": {
        "type": "Reefer Trailer",
        "brake_wear_pct": 78,
        "tyre_pressure_bar": 7.2,
        "tru_fault_code": "TRU-E04",
        "last_service_days_ago": 42,
        "mileage_since_service": 12400,
    },
    "TRL-002": {
        "type": "Curtainsider",
        "brake_wear_pct": 31,
        "tyre_pressure_bar": 8.1,
        "tru_fault_code": None,
        "last_service_days_ago": 12,
        "mileage_since_service": 3200,
    },
    "TNK-001": {
        "type": "Bulk Tanker",
        "brake_wear_pct": 65,
        "tyre_pressure_bar": 6.8,
        "tru_fault_code": None,
        "last_service_days_ago": 30,
        "mileage_since_service": 9800,
    },
}


# ── Parts Tools ───────────────────────────────────────────────────────────────
def check_parts_availability(part_name: str) -> dict:
    """
    Checks depot stock levels for a specific part.

    Args:
        part_name: The name of the part to check

    Returns:
        Stock levels across all depots, unit cost, and lead time.
    """
    matched = None
    for key in PARTS_INVENTORY:
        if part_name.lower() in key.lower() or key.lower() in part_name.lower():
            matched = key
            break
    if not matched:
        return {
            "error": f"Part '{part_name}' not found.",
            "available_parts": list(PARTS_INVENTORY.keys()),
        }
    stock = PARTS_INVENTORY[matched]
    in_stock_depots = {
        depot: qty
        for depot, qty in stock.items()
        if depot.startswith("depot") and qty > 0
    }
    return {
        "part": matched,
        "unit_cost_gbp": stock["unit_cost_gbp"],
        "lead_time_days": stock["lead_time_days"],
        "stock_by_depot": {k: v for k, v in stock.items() if k.startswith("depot")},
        "available_at": in_stock_depots if in_stock_depots else "OUT OF STOCK at all depots",
        "pre_position_signal": (
            "SEND PRE-POSITION ORDER NOW — stock critically low, lead time > 0 days"
            if stock["lead_time_days"] > 0 or not in_stock_depots
            else "Stock sufficient — no pre-order needed"
        ),
    }


def check_parts_for_job_card(parts_list: str) -> dict:
    """
    Checks availability for all parts required by a job card at once.

    Args:
        parts_list: Comma-separated list of parts needed

    Returns:
        Consolidated availability report with total estimated cost.
    """
    parts = [p.strip() for p in parts_list.split(",")]
    results = []
    total_cost = 0
    has_shortage = False
    for part in parts:
        availability = check_parts_availability(part)
        if "error" not in availability:
            total_cost += availability["unit_cost_gbp"]
            if (
                availability["lead_time_days"] > 0
                or availability["available_at"] == "OUT OF STOCK at all depots"
            ):
                has_shortage = True
        results.append(availability)
    return {
        "parts_checked": len(parts),
        "total_estimated_cost_gbp": total_cost,
        "shortage_alert": has_shortage,
        "pre_position_required": has_shortage,
        "parts_detail": results,
        "recommendation": (
            "PARTS PRE-POSITION ORDER REQUIRED — place order 48-72 hours before asset arrives."
            if has_shortage
            else "All parts in stock. Asset can be serviced immediately on arrival."
        ),
    }


# ── Parts Sub-Agent ───────────────────────────────────────────────────────────
parts_agent = Agent(
    name="parts_inventory_agent",
    model="gemini-1.5-flash",
    description="Specialist sub-agent for parts and inventory management across depots.",
    instruction="""
You are the Parts and Inventory Agent for a trailer and tanker leasing company.
Check stock availability for parts across all depots.
Report unit costs and lead times clearly.
Flag parts that need pre-ordering prominently.
Use check_parts_for_job_card when checking multiple parts at once.
""",
    tools=[check_parts_availability, check_parts_for_job_card],
)


# ── Orchestrator Tools ────────────────────────────────────────────────────────
def get_asset_telemetry(asset_id: str) -> dict:
    """
    Fetches current telemetry data for a given asset.

    Args:
        asset_id: The unique asset identifier e.g. TRL-001

    Returns:
        Dictionary with current sensor readings for the asset.
    """
    asset_id = asset_id.upper().strip()
    if asset_id not in ASSET_DB:
        return {
            "error": f"Asset {asset_id} not found.",
            "available_assets": list(ASSET_DB.keys()),
        }
    return {"asset_id": asset_id, **ASSET_DB[asset_id]}


def predict_failure_risk(asset_id: str) -> dict:
    """
    Predicts component failure risk for a given asset.

    Args:
        asset_id: The unique asset identifier

    Returns:
        Risk assessment with component-level scores and overall priority.
    """
    asset_id = asset_id.upper().strip()
    if asset_id not in ASSET_DB:
        return {"error": f"Asset {asset_id} not found."}
    data = ASSET_DB[asset_id]
    risks = []
    if data["brake_wear_pct"] > 70:
        risks.append({
            "component": "Brake System",
            "risk_level": "HIGH",
            "estimated_days_to_failure": 3,
            "recommended_action": "Immediate brake adjustment or pad replacement",
            "parts_needed": "Brake pads x4, Brake adjustment kit",
        })
    elif data["brake_wear_pct"] > 50:
        risks.append({
            "component": "Brake System",
            "risk_level": "MEDIUM",
            "estimated_days_to_failure": 10,
            "recommended_action": "Schedule brake inspection within 7 days",
            "parts_needed": "Brake pads x4",
        })
    if data["tyre_pressure_bar"] < 7.0:
        risks.append({
            "component": "Tyres",
            "risk_level": "HIGH",
            "estimated_days_to_failure": 2,
            "recommended_action": "Immediate tyre pressure check and inflation",
            "parts_needed": "Nitrogen inflation kit, Tyre pressure gauge",
        })
    if data["tru_fault_code"]:
        risks.append({
            "component": "Refrigeration Unit (TRU)",
            "risk_level": "HIGH",
            "estimated_days_to_failure": 1,
            "recommended_action": f"TRU fault {data['tru_fault_code']} detected — inspect compressor immediately",
            "parts_needed": "Refrigerant R-404A, Compressor filter kit",
        })
    priority = "LOW"
    if any(r["risk_level"] == "HIGH" for r in risks):
        priority = "CRITICAL"
    elif any(r["risk_level"] == "MEDIUM" for r in risks):
        priority = "MODERATE"
    all_parts = ", ".join(r["parts_needed"] for r in risks if r.get("parts_needed"))
    return {
        "asset_id": asset_id,
        "asset_type": data["type"],
        "overall_priority": priority,
        "component_risks": risks if risks else [{
            "component": "All Systems",
            "risk_level": "LOW",
            "recommended_action": "No immediate action required",
            "parts_needed": "",
        }],
        "all_parts_needed": all_parts,
    }


def generate_job_card(asset_id: str) -> dict:
    """
    Auto-generates a structured repair job card for a given asset.

    Args:
        asset_id: The unique asset identifier

    Returns:
        Structured job card with jobs and parts required.
    """
    risk = predict_failure_risk(asset_id)
    if "error" in risk:
        return risk
    jobs = [
        f"[{c['risk_level']}] {c['component']}: {c['recommended_action']}"
        for c in risk["component_risks"]
        if c["risk_level"] in ("HIGH", "MEDIUM")
    ]
    return {
        "job_card_id": f"JC-{asset_id}-AUTO",
        "asset_id": asset_id,
        "asset_type": risk["asset_type"],
        "priority": risk["overall_priority"],
        "jobs": jobs if jobs else ["No urgent jobs — routine inspection only"],
        "parts_required": risk["all_parts_needed"] or "None",
        "depot_routing": "Depot A — Manchester (nearest depot)",
        "technician_skill": "Heavy Vehicle Technician Level 2",
        "status": "PENDING PARTS CONFIRMATION",
    }


def get_fleet_risk_summary() -> dict:
    """
    Returns a ranked risk summary across all assets in the fleet.

    Returns:
        All assets ranked by overall priority with top risks highlighted.
    """
    summary = []
    for asset_id in ASSET_DB:
        risk = predict_failure_risk(asset_id)
        summary.append({
            "asset_id": asset_id,
            "asset_type": risk["asset_type"],
            "priority": risk["overall_priority"],
            "top_risk": risk["component_risks"][0]["component"] if risk["component_risks"] else "None",
            "parts_needed": risk.get("all_parts_needed", "None"),
        })
    priority_order = {"CRITICAL": 0, "MODERATE": 1, "LOW": 2}
    summary.sort(key=lambda x: priority_order.get(x["priority"], 3))
    return {
        "total_assets": len(summary),
        "critical_count": sum(1 for a in summary if a["priority"] == "CRITICAL"),
        "moderate_count": sum(1 for a in summary if a["priority"] == "MODERATE"),
        "fleet_ranked": summary,
    }


# ── Root Orchestrator Agent ───────────────────────────────────────────────────
root_agent = Agent(
    name="fleetsense_orchestrator",
    model="gemini-1.5-flash",
    description="TCS FleetSense Orchestrator: coordinates asset health assessment and delegates parts queries to the Parts Inventory sub-agent.",
    instruction="""
You are FleetSense, the AI maintenance orchestrator for a trailer and tanker leasing company.
You coordinate with a specialist Parts Inventory sub-agent for complete maintenance decisions.

Your workflow for ANY asset query:
1. Fetch telemetry with get_asset_telemetry
2. Predict failure risk with predict_failure_risk
3. If risk is MEDIUM or CRITICAL generate a job card with generate_job_card
4. Always delegate parts availability to the parts_inventory_agent sub-agent
5. Combine both results into a final recommendation for the technician

For fleet overview queries use get_fleet_risk_summary then check parts for CRITICAL assets.
Available assets: TRL-001 (Reefer Trailer), TRL-002 (Curtainsider), TNK-001 (Bulk Tanker)
Be concise and direct. Lead with priority and action.
""",
    tools=[get_asset_telemetry, predict_failure_risk, generate_job_card, get_fleet_risk_summary],
    sub_agents=[parts_agent],
)

Thursday, November 26, 2015

The Way Forgotten

We Indians have forgotten the way we lived once a upon a time. Westernized, influence of various civilization who invaded us and ruled us; has just given way to a hybrid culture and civilization where we speak English as an official language which is not the national language at office. It is evident because, the very post which I have written now is in English. 

We consider people who know English superior and people with native languages inferior. It is a victory of Britishers and the defeat for ourselves. If you check history English has been around only for the past 200 years in India. Before that we had so many native languages and the base language of all was Sanskrit. 

How many people know Sanskrit today, other than the broken Sanskrit students learn at school as a scoring subject. Not many even know that Sanskrit is the official language of Uttarakhand. Do you know how many people around the world want to learn Sanskrit and its hidden fascinating meanings? 

Germany have so many universities offering Sanskrit as a subject. People from NASA have a separate wing of scientist studying the secrets of Sanskrit. The word is out that the next generation computers will have Sanskrit as their base language, as almost or all the languages in the world have been derived from it. Sanskrit is part of the subjects taught in a school in England, which produces the best ranking students every year. They say Sanskrit improves brain functionality and hence intelligence and IQ. 

No wonder, our Rishis and Maharisihis where world renowned scholars. Sanskrit was not a language of rishis and maharishis alone, it was the native mother tongue of ancient Indian Subcontinent. There are villages in India, who still speak Sanskrit. We are easily baffled by the western world. How many of us take the time to understand our forgotten fore fathers and the way we used to live and thrive. 

If Aliens would have visited the ages of Munis and if they happen to come back again to the Holy land of India, they would never believe that we are the descendants of the great Aryan civilization where the Rig veda once flourished. Even today there are Sanskrit scholars who teach the language of our fore fathers. I would like to leave a thought in your mind with the following shlok. 

Image description not specified.

Would you believe me, if I tell you this shlok when decrypted can tell the value of Pi upto 31 decimal places. Sanskrit is just mind boggling language. Learn Sanskrit and teach your next generation. After all we can still get back few things which we have forgotten. 

Tuesday, December 2, 2014

Life changing experience

Kris Gethin and Hirthik Roshan, my role models. Wanted to follow in their foot steps. Took up a workout routine of  Kris for 12 weeks. I broke down this into 3 weeks and a break of 2 weeks and again 3 weeks and 2 weeks and so on. 

It was an awesome feeling to see some muscles develop after so much hard work at the gym. My weight has not decreased drastically, neither has my appetite. My waist size has reduced from 38 inches to around 35 inches now. The fat I have lost are the muscles I have gained, so the weight got compensated. 


Workouts are more intense now a days. Have gone into the high 70lb levels now. Weight training has its plus. During the 3 weeks on, I did not miss gym even for a day. On rest days, when I am not doing weight training, I went for a run. Nike running app is really a cool app, it tracks all my runs.  




Bodyspace app keeps good track of your workouts. Could track all my workouts and store for later analysis. I used every fitness app which I could get hold of and used it to my advantage. I never thought I would get the six pack definition. But it is part of the diet I followed. From someone who suffered from acidity and breathlessness, to someone who can pic up the 100 lbs dumbbell without a sweat, or okay with a sweat ;) My fitness journey is here to stay for a long time now. I found passion in it. It's time you thought about fitness, before it is too late.










Powered By Blogger