39145-vm/core/ai_views.py
2026-03-12 16:08:07 +00:00

145 lines
6.6 KiB
Python

from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
from .models import Project, MindMapNode, MindMapConnection
from ai.local_ai_api import LocalAIApi
from django.shortcuts import get_object_or_404
import json
@csrf_exempt
@login_required
def ai_chat(request, pk):
if request.method == 'POST':
project = get_object_or_404(Project, pk=pk, user=request.user)
try:
data = json.loads(request.body)
user_message = data.get('message')
# Context builder for the project
nodes = list(project.nodes.values('id', 'title', 'category'))
nodes_context = json.dumps(nodes)
system_prompt = f"""You are an AI business strategist helping a user build a mind map for their project.
Project Title: {project.title}
Industry: {project.industry}
Goal: {project.goal}
Current Nodes (with IDs):
{nodes_context}
Respond ONLY with a valid JSON object in the following format:
{{
"message": "Your text response to the user's prompt goes here.",
"new_nodes": [
{{"id": "temp_1", "title": "New Idea", "summary": "Short description", "category": "Strategy"}}
],
"new_connections": [
{{"source_id": 1, "target_id": "temp_1", "how": "Relates to existing node", "why": "Important reason"}}
]
}}
Instructions:
- If the user just asks a question, put your answer in "message" and leave "new_nodes" and "new_connections" empty.
- If the user asks to add nodes, brainstorm nodes, or expand the map, generate them and place them in "new_nodes". Use temporary string IDs for new nodes (e.g., "temp_1").
- For connections, "source_id" and "target_id" can be an existing integer ID from the Current Nodes list OR a temporary string ID from "new_nodes".
- Keep your answers concise and practical.
"""
response = LocalAIApi.create_response({
"input": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_message}
],
"text": {"format": {"type": "json_object"}}
})
if response.get("success"):
ai_data = LocalAIApi.decode_json_from_response(response)
if not ai_data:
# fallback if it's not valid JSON
ai_text = LocalAIApi.extract_text(response)
return JsonResponse({'response': ai_text})
# Extract parts
ai_message = ai_data.get("message", "I have updated the mind map.")
new_nodes_data = ai_data.get("new_nodes", [])
new_connections_data = ai_data.get("new_connections", [])
added_nodes = []
added_connections = []
if new_nodes_data or new_connections_data:
# Save nodes
temp_to_real_id = {}
for n in new_nodes_data:
node = MindMapNode.objects.create(
project=project,
title=n.get("title", "Untitled"),
summary=n.get("summary", ""),
category=n.get("category", "General")
)
temp_to_real_id[n.get("id")] = node
added_nodes.append({
"id": node.pk,
"title": node.title,
"summary": node.summary,
"category": node.category
})
# Create dictionary to fetch existing nodes quickly by int ID
existing_nodes_map = {n.id: n for n in project.nodes.all()}
# Save connections
for c in new_connections_data:
source_ref = c.get("source_id")
target_ref = c.get("target_id")
source_node = None
target_node = None
# Resolve source
if isinstance(source_ref, int) and source_ref in existing_nodes_map:
source_node = existing_nodes_map[source_ref]
elif isinstance(source_ref, str) and str(source_ref).isdigit() and int(source_ref) in existing_nodes_map:
source_node = existing_nodes_map[int(source_ref)]
elif source_ref in temp_to_real_id:
source_node = temp_to_real_id[source_ref]
# Resolve target
if isinstance(target_ref, int) and target_ref in existing_nodes_map:
target_node = existing_nodes_map[target_ref]
elif isinstance(target_ref, str) and str(target_ref).isdigit() and int(target_ref) in existing_nodes_map:
target_node = existing_nodes_map[int(target_ref)]
elif target_ref in temp_to_real_id:
target_node = temp_to_real_id[target_ref]
if source_node and target_node:
conn = MindMapConnection.objects.create(
project=project,
source=source_node,
target=target_node,
how=c.get("how", ""),
why=c.get("why", "")
)
added_connections.append({
"id": conn.pk,
"source_id": source_node.pk,
"target_id": target_node.pk,
"how": conn.how,
"why": conn.why
})
return JsonResponse({
'response': ai_message,
'added_nodes': added_nodes,
'added_connections': added_connections
})
else:
return JsonResponse({'response': "Sorry, I had trouble processing that request. Please try again."})
except json.JSONDecodeError:
return JsonResponse({'error': 'Invalid request body'}, status=400)
return JsonResponse({'error': 'Invalid request method'}, status=405)