37622-vm/core/views.py
Flatlogic Bot 2a479c82a7 2.2
2026-01-20 17:13:37 +00:00

188 lines
7.0 KiB
Python

from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from .models import Business, Service, Call, Booking, Contact
from django.db.models import Count
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
import json
from twilio.twiml.voice_response import VoiceResponse, Gather
from django.urls import reverse
from ai.local_ai_api import LocalAIApi
from .booking import create_booking
from datetime import datetime
#@login_required
def dashboard(request):
"""Render the main admin dashboard."""
businesses = Business.objects.annotate(
service_count=Count('services', distinct=True),
call_count=Count('calls', distinct=True),
booking_count=Count('services__bookings', distinct=True)
)
recent_calls = Call.objects.order_by('-start_time')[:10]
upcoming_bookings = Booking.objects.filter(start_time__gte=datetime.now()).order_by('start_time')[:10]
context = {
'businesses': businesses,
'recent_calls': recent_calls,
'upcoming_bookings': upcoming_bookings,
'page_title': 'Dashboard - Xfront'
}
return render(request, 'core/index.html', context)
def business_detail(request, business_id):
"""Display a detailed view of a business."""
business = get_object_or_404(Business, pk=business_id)
services = business.services.all()
calls = business.calls.all().order_by('-start_time')[:10]
bookings = Booking.objects.filter(service__business=business).order_by('-start_time')[:10]
context = {
'business': business,
'services': services,
'calls': calls,
'bookings': bookings,
'page_title': f'{business.business_name} - Details'
}
return render(request, 'core/business_detail.html', context)
@csrf_exempt
def inbound_call_webhook(request):
"""Handle inbound calls from Twilio."""
if request.method == 'POST':
from_number = request.POST.get('From')
to_number = request.POST.get('To')
try:
business = Business.objects.get(phone_number=to_number)
except Business.DoesNotExist:
response = VoiceResponse()
response.say("The number you have called is not associated with a business.")
return HttpResponse(str(response), content_type='text/xml')
contact, _ = Contact.objects.get_or_create(phone_number=from_number)
call = Call.objects.create(
business=business,
contact=contact,
)
response = VoiceResponse()
response.say(f"Hello and welcome to {business.business_name}. Please wait while we connect you to our AI assistant.")
response.redirect(reverse('ai_call_handler', args=[call.id]))
return HttpResponse(str(response), content_type='text/xml')
return HttpResponse(status=400)
tools = [
{
"type": "function",
"function": {
"name": "create_booking",
"description": "Creates a booking for a given service at a specific time.",
"parameters": {
"type": "object",
"properties": {
"service_name": {
"type": "string",
"description": "The name of the service to book.",
},
"booking_time_str": {
"type": "string",
"description": "The desired time for the booking in ISO 8601 format.",
},
},
"required": ["service_name", "booking_time_str"],
},
},
}
]
@csrf_exempt
def ai_call_handler(request, call_id):
"""Handle the AI-powered conversation."""
call = get_object_or_404(Call, pk=call_id)
response = VoiceResponse()
if 'SpeechResult' in request.POST:
user_speech = request.POST['SpeechResult']
call.conversation_history.append({'role': 'user', 'content': user_speech})
services = Service.objects.filter(business=call.business)
services_prompt = "\n".join([f"- {s.name}: {s.description}" for s in services])
# Get AI response
ai_response = LocalAIApi.create_response(
{
"input": [
{
"role": "system",
"content": f"You are a friendly and helpful AI assistant for {call.business.business_name}. Available services are:\n{services_prompt}"
},
*call.conversation_history
],
"tools": tools,
"tool_choice": "auto",
},
{
"poll_interval": 5,
"poll_timeout": 300,
},
)
if ai_response.get("success") and ai_response.get("data", {}).get("output", [{}])[0].get("content", [{}])[0].get("type") == "tool_calls":
tool_calls = ai_response["data"]["output"][0]["content"][0]["tool_calls"]
call.conversation_history.append({"role": "assistant", "content": None, "tool_calls": tool_calls})
for tool_call in tool_calls:
function_name = tool_call['function']['name']
if function_name == 'create_booking':
args = json.loads(tool_call['function']['arguments'])
result = create_booking(
contact_phone_number=call.contact.phone_number,
service_name=args.get("service_name"),
booking_time_str=args.get("booking_time_str"),
)
call.conversation_history.append({"role": "tool", "tool_call_id": tool_call['id'], "name": function_name, "content": result})
# Get AI response after tool call
ai_response = LocalAIApi.create_response(
{
"input": [
{
"role": "system",
"content": f"You are a friendly and helpful AI assistant for {call.business.business_name}. Available services are:\n{services_prompt}"
},
*call.conversation_history
],
"tools": tools,
"tool_choice": "auto",
},
{
"poll_interval": 5,
"poll_timeout": 300,
},
)
ai_text = LocalAIApi.extract_text(ai_response)
else:
ai_text = LocalAIApi.extract_text(ai_response)
if not ai_text:
ai_text = "I am sorry, I am having trouble understanding. Please try again."
call.conversation_history.append({'role': 'assistant', 'content': ai_text})
response.say(ai_text)
else:
# First turn of the conversation
response.say("How can I help you today?")
gather = Gather(input='speech', action=reverse('ai_call_handler', args=[call.id]), speechTimeout='auto')
response.append(gather)
call.save()
return HttpResponse(str(response), content_type='text/xml')