Spaces:
Running on Zero
Running on Zero
| #!/usr/bin/env python3 | |
| """ | |
| scripts/omniroute_freight_synthesizer.py — OmniRoute 'paid-premium' Multi-Agent Freight Dataset Synthesizer. | |
| Generates hyper-realistic multi-turn freight negotiation training dialogues using OmniRoute's | |
| high-reasoning 'paid-premium' combo model. | |
| Output complies with: | |
| - OpenAI tool-calling chat completions format | |
| - LoadETA tool schemas (fmcsa_verify_mc, calculate_rate_floor, get_truck_route_distance, book_load_offer) | |
| - LiveKit voice conversational requirements (phonetic currencies, spoken cadence, no markdown) | |
| Usage: | |
| python3 scripts/omniroute_freight_synthesizer.py --count 10 --output data/freight_negotiation_omniroute.jsonl | |
| """ | |
| import os | |
| import sys | |
| import json | |
| import random | |
| import time | |
| import argparse | |
| import urllib.request | |
| import urllib.error | |
| from typing import Dict, Any, List | |
| OMNIROUTE_URL = "http://100.70.158.21:20128/v1/chat/completions" | |
| OMNIROUTE_MODEL = "paid-premium" | |
| SEED_SCENARIOS = [ | |
| { | |
| "lane": "Atlanta, GA to Chicago, IL", | |
| "miles": 715, | |
| "equipment": "53ft Reefer", | |
| "temp": "-10F continuous", | |
| "commodity": "Frozen Poultry", | |
| "weight": "43,000 lbs", | |
| "broker_persona": "Aggressive lowball broker trying to anchor rate at $1,800 on a $2,400 lane", | |
| "special_condition": "Requires strict 2 hours free detention agreement and temp recorder verification", | |
| }, | |
| { | |
| "lane": "Laredo, TX to Dallas, TX", | |
| "miles": 430, | |
| "equipment": "53ft Dry Van", | |
| "temp": "ambient", | |
| "commodity": "Cross-border Automotive Parts", | |
| "weight": "42,500 lbs", | |
| "broker_persona": "Urgent hot-load broker with auto assembly line shutdown risk if not picked up in 90 mins", | |
| "special_condition": "High rate elasticity, dispatcher pushes for premium rate ($1,650)", | |
| }, | |
| { | |
| "lane": "Allentown, PA to Richmond, VA", | |
| "miles": 260, | |
| "equipment": "48ft Flatbed", | |
| "temp": "ambient", | |
| "commodity": "Structural Steel & Beams with 8ft Tarp", | |
| "weight": "46,000 lbs", | |
| "broker_persona": "Mid-tier broker trying to avoid paying $150 tarp fee", | |
| "special_condition": "Dispatcher stands firm on tarping surcharge and Northeast toll reimbursement", | |
| }, | |
| { | |
| "lane": "Los Angeles, CA to Phoenix, AZ", | |
| "miles": 375, | |
| "equipment": "53ft Reefer", | |
| "temp": "+34F pre-cooled", | |
| "commodity": "Fresh Organic Berries", | |
| "weight": "38,000 lbs", | |
| "broker_persona": "Double-broker suspect with newly registered MC (42 days old)", | |
| "special_condition": "FMCSA tool flags new authority and lack of credit score; dispatcher politely refuses load", | |
| }, | |
| { | |
| "lane": "Houston, TX to Savannah, GA", | |
| "miles": 840, | |
| "equipment": "53ft Dry Van", | |
| "temp": "ambient", | |
| "commodity": "Retail Consumer Goods", | |
| "weight": "36,000 lbs", | |
| "broker_persona": "Standard C.H. Robinson corporate broker negotiating multi-stop delivery", | |
| "special_condition": "2 pick-up locations and 2 drop-offs; dispatcher negotiates $100 extra per stop", | |
| }, | |
| ] | |
| def get_omniroute_api_key() -> str: | |
| key = os.environ.get("OMNIROUTE_API_KEY") | |
| if key: | |
| return key | |
| secrets_path = os.path.expanduser("~/.mcp/secrets/omniroute.env") | |
| if os.path.exists(secrets_path): | |
| with open(secrets_path, "r", encoding="utf-8") as f: | |
| for line in f: | |
| if line.startswith("OMNIROUTE_MCP_KEY="): | |
| return line.strip().split("=", 1)[1] | |
| return "" | |
| def call_omniroute(prompt: str, system: str = "") -> str: | |
| key = get_omniroute_api_key() | |
| headers = { | |
| "Authorization": f"Bearer {key}", | |
| "Content-Type": "application/json", | |
| } | |
| payload = { | |
| "model": OMNIROUTE_MODEL, | |
| "messages": [ | |
| {"role": "system", "content": system or "You are an expert synthetic dataset engineer for autonomous freight negotiation voice agents."}, | |
| {"role": "user", "content": prompt}, | |
| ], | |
| "temperature": 0.7, | |
| "max_tokens": 2500, | |
| } | |
| req = urllib.request.Request( | |
| OMNIROUTE_URL, | |
| data=json.dumps(payload).encode("utf-8"), | |
| headers=headers, | |
| ) | |
| with urllib.request.urlopen(req, timeout=120) as resp: | |
| res = json.loads(resp.read().decode("utf-8")) | |
| return res["choices"][0]["message"]["content"] | |
| def extract_json(raw_text: str) -> Dict[str, Any]: | |
| import re | |
| # 1. Try markdown code blocks first (often the cleanest JSON) | |
| blocks = re.findall(r"```(?:json)?\s*([\s\S]*?)\s*```", raw_text) | |
| for block in reversed(blocks): | |
| try: | |
| val = json.loads(block.strip()) | |
| if isinstance(val, dict) and ("messages" in val or "turns" in val or "id" in val): | |
| return val | |
| except Exception: | |
| pass | |
| for block in blocks: | |
| try: | |
| val = json.loads(block.strip()) | |
| if isinstance(val, dict): | |
| return val | |
| except Exception: | |
| pass | |
| # 2. Try whole string directly | |
| try: | |
| return json.loads(raw_text.strip()) | |
| except Exception: | |
| pass | |
| # 3. Scan from first { to last } | |
| s = raw_text.find("{") | |
| e = raw_text.rfind("}") | |
| if s != -1 and e != -1 and e > s: | |
| try: | |
| return json.loads(raw_text[s:e+1]) | |
| except Exception: | |
| pass | |
| raise ValueError(f"No valid JSON object could be extracted from: {raw_text[:120]}") | |
| def synthesize_conversation(scenario: Dict[str, Any], index: int) -> Dict[str, Any]: | |
| prompt = f""" | |
| Generate an authentic, complete multi-turn freight negotiation training conversation between a US Freight Broker and LoadETA (an autonomous dispatcher voice agent). | |
| Scenario Parameters: | |
| - Lane: {scenario['lane']} ({scenario['miles']} miles) | |
| - Equipment: {scenario['equipment']} | |
| - Commodity & Weight: {scenario['commodity']} ({scenario['weight']}) | |
| - Temperature: {scenario['temp']} | |
| - Broker Profile: {scenario['broker_persona']} | |
| - Special Case: {scenario['special_condition']} | |
| Strict Rules for LoadETA Spoken Output: | |
| 1. Format exclusively for human voice synthesis (LiveKit TTS): No markdown bolding, no bullet points, no emojis, no robotic phrasing. | |
| 2. Spoken numbers and currencies: use natural spoken words for money (e.g. "twenty-four hundred", "eighteen fifty all in", "seventy-five dollars an hour"). | |
| 3. Tool Calling: Assistant MUST include tool calls with exact arguments: | |
| - 'fmcsa_verify_mc' with arguments {{"mc_number": "...", "broker_name": "..."}} | |
| - 'calculate_rate_floor' with arguments {{"origin": "...", "destination": "...", "equipment_type": "...", "mileage": {scenario['miles']}, "weight": 42000}} | |
| - 'book_load_offer' with arguments {{"broker_name": "...", "mc_number": "...", "rate_agreed": 2400, "detention_rate_per_hr": 75}} | |
| You must output a JSON object with this exact key structure: | |
| {{ | |
| "id": "omniroute_freight_{index:05d}", | |
| "messages": [ | |
| {{"role": "system", "content": "You are LoadETA, an expert freight dispatcher and live negotiation voice agent for US commercial trucking. Protect driver margins, verify broker authority, and negotiate rates naturally for voice without markdown."}}, | |
| {{"role": "user", "content": "Broker opening pitch..."}}, | |
| {{"role": "assistant", "content": null, "tool_calls": [{{"id": "call_fmcsa_{index:04d}", "type": "function", "function": {{"name": "fmcsa_verify_mc", "arguments": "{{\\"mc_number\\": \\"782914\\", \\"broker_name\\": \\"Apex Logistics\\"}}"}}}}]}}, | |
| {{"role": "tool", "tool_call_id": "call_fmcsa_{index:04d}", "content": "{{\\"status\\": \\"CLEAN\\", \\"active\\": true, \\"credit_score\\": 94}}"}} | |
| {{"role": "assistant", "content": "Natural counter-offer in spoken voice..."}}, | |
| {{"role": "user", "content": "Broker counter..."}}, | |
| {{"role": "assistant", "content": null, "tool_calls": [{{"id": "call_book_{index:04d}", "type": "function", "function": {{"name": "book_load_offer", "arguments": "{{\\"rate_agreed\\": 2400}}"}}}}]}}, | |
| {{"role": "tool", "tool_call_id": "call_book_{index:04d}", "content": "{{\\"status\\": \\"BOOKED\\"}}"}}, | |
| {{"role": "assistant", "content": "Final confirmation..."}} | |
| ] | |
| }} | |
| OUTPUT JSON ONLY. Do not write explanations or notes. | |
| """ | |
| raw_response = call_omniroute(prompt) | |
| try: | |
| data = extract_json(raw_response) | |
| return data | |
| except Exception as e: | |
| print(f"JSON decode failed for scenario {index}: {e}. Raw head: {repr(raw_response[:100])}") | |
| return None | |
| def main(): | |
| parser = argparse.ArgumentParser(description="OmniRoute Paid-Premium Freight Dataset Synthesizer") | |
| parser.add_argument("--count", type=int, default=5, help="Number of dialogues to synthesize") | |
| parser.add_argument("--output", default="freight/data/freight_negotiation_omniroute.jsonl", help="Output JSONL file") | |
| args = parser.parse_args() | |
| os.makedirs(os.path.dirname(args.output), exist_ok=True) | |
| print(f"Connecting to OmniRoute ({OMNIROUTE_URL}) with model '{OMNIROUTE_MODEL}'...") | |
| results = [] | |
| for i in range(1, args.count + 1): | |
| scenario = random.choice(SEED_SCENARIOS) | |
| print(f"[{i}/{args.count}] Synthesizing: {scenario['lane']} ({scenario['equipment']})...") | |
| conv = synthesize_conversation(scenario, i) | |
| if conv: | |
| results.append(conv) | |
| with open(args.output, "a" if i > 1 else "w", encoding="utf-8") as f: | |
| f.write(json.dumps(conv) + "\n") | |
| print(f" -> Generated {len(conv.get('messages', []))} turns.") | |
| time.sleep(1) | |
| print(f"\nSuccessfully generated {len(results)} high-reasoning dialogues in {args.output}") | |
| if __name__ == "__main__": | |
| main() | |