import asyncio import json from agents.negotiator_agent import NegotiatorAgent import database as db async def run_negotiation(negotiation_id: str, preferences_a: dict, preferences_b: dict, user_a_id: int, user_b_id: int, feature_type: str, personality_a: str = "balanced", personality_b: str = "balanced", on_round_update=None, on_resolution=None, feature_context: str = ""): """ Main negotiation loop with personality-aware agents and analytics tracking. """ await db.update_negotiation_status(negotiation_id, "active") # Create personality-aware negotiators negotiator_a = NegotiatorAgent(personality=personality_a) negotiator_b = NegotiatorAgent(personality=personality_b) current_proposal = None max_rounds = 7 satisfaction_timeline = [] for round_num in range(1, max_rounds + 1): await asyncio.sleep(1.5) # Rate limit protection for Gemini if round_num == 1: response = await negotiator_a.generate_initial_proposal( my_preferences=preferences_a, feature_type=feature_type, feature_context=feature_context ) proposer_id = user_a_id elif round_num % 2 == 0: response = await negotiator_b.evaluate_and_respond( received_proposal=current_proposal, my_preferences=preferences_b, feature_type=feature_type, round_number=round_num, feature_context=feature_context ) proposer_id = user_b_id else: response = await negotiator_a.evaluate_and_respond( received_proposal=current_proposal, my_preferences=preferences_a, feature_type=feature_type, round_number=round_num, feature_context=feature_context ) proposer_id = user_a_id # Handle errors if "error" in response: response = { "action": "counter" if round_num < max_rounds else "escalate", "proposal": current_proposal or {"summary": "Let's discuss further", "details": {}}, "satisfaction_score": 50, "reasoning": "Agent encountered an issue", "concessions_made": [], "concessions_requested": [] } action = response.get("action", "counter") current_proposal = response.get("proposal", {}) satisfaction = response.get("satisfaction_score", 50) concessions = response.get("concessions_made", []) # Track satisfaction for analytics # The proposer's score is the one returned; estimate the other party's if proposer_id == user_a_id: sat_a, sat_b = satisfaction, max(30, 100 - satisfaction * 0.4) else: sat_b, sat_a = satisfaction, max(30, 100 - satisfaction * 0.4) satisfaction_timeline.append({ "round": round_num, "score_a": sat_a, "score_b": sat_b }) # Save round with analytics data await db.save_round( negotiation_id=negotiation_id, round_number=round_num, proposer_id=proposer_id, proposal=response, response_type=action, reasoning=response.get("reasoning", ""), satisfaction_a=sat_a, satisfaction_b=sat_b, concessions_made=concessions ) # Notify via callback round_data = { "negotiation_id": negotiation_id, "round_number": round_num, "action": action, "proposal": current_proposal, "satisfaction_score": satisfaction, "reasoning": response.get("reasoning", ""), "proposer_id": proposer_id, "satisfaction_a": sat_a, "satisfaction_b": sat_b } if on_round_update: await on_round_update(round_data) # Check outcome if action == "accept": resolution = { "status": "resolved", "final_proposal": current_proposal, "rounds_taken": round_num, "summary": current_proposal.get("summary", "Agreement reached"), "satisfaction_timeline": satisfaction_timeline } await db.update_negotiation_status(negotiation_id, "resolved", resolution) if on_resolution: await on_resolution(resolution) return resolution if action == "escalate": resolution = { "status": "escalated", "final_proposal": current_proposal, "rounds_taken": round_num, "summary": "Agents couldn't fully agree. Options for human decision.", "satisfaction_timeline": satisfaction_timeline } await db.update_negotiation_status(negotiation_id, "escalated", resolution) if on_resolution: await on_resolution(resolution) return resolution # Exhausted rounds resolution = { "status": "escalated", "final_proposal": current_proposal, "rounds_taken": max_rounds, "summary": "Max rounds reached. Best proposal for human decision.", "satisfaction_timeline": satisfaction_timeline } await db.update_negotiation_status(negotiation_id, "escalated", resolution) if on_resolution: await on_resolution(resolution) return resolution