diff --git a/ai/__pycache__/__init__.cpython-311.pyc b/ai/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..977e624 Binary files /dev/null and b/ai/__pycache__/__init__.cpython-311.pyc differ diff --git a/ai/__pycache__/local_ai_api.cpython-311.pyc b/ai/__pycache__/local_ai_api.cpython-311.pyc new file mode 100644 index 0000000..c4653af Binary files /dev/null and b/ai/__pycache__/local_ai_api.cpython-311.pyc differ diff --git a/core/__pycache__/ai_helpers.cpython-311.pyc b/core/__pycache__/ai_helpers.cpython-311.pyc new file mode 100644 index 0000000..38e324a Binary files /dev/null and b/core/__pycache__/ai_helpers.cpython-311.pyc differ diff --git a/core/__pycache__/ai_views.cpython-311.pyc b/core/__pycache__/ai_views.cpython-311.pyc index f43e924..6bb38c2 100644 Binary files a/core/__pycache__/ai_views.cpython-311.pyc and b/core/__pycache__/ai_views.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 3046e6b..587c879 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/ai_helpers.py b/core/ai_helpers.py new file mode 100644 index 0000000..67e03af --- /dev/null +++ b/core/ai_helpers.py @@ -0,0 +1,60 @@ +from ai.local_ai_api import LocalAIApi +from .models import MindMapNode, MindMapConnection +import json + +def generate_initial_mindmap(project): + prompt = f""" +You are an expert business consultant. Create an initial mind map for a new project. +Project Title: {project.title} +Industry: {project.industry} +Goal: {project.goal} + +Respond ONLY with a valid JSON object in the following format: +{{ + "nodes": [ + {{"id": "node_1", "title": "Main Goal", "summary": "Short description", "category": "Strategy"}}, + {{"id": "node_2", "title": "Feature X", "summary": "Short description", "category": "Product"}} + ], + "connections": [ + {{"source_id": "node_1", "target_id": "node_2", "how": "Defines what to build", "why": "Feature X is critical to achieve the Main Goal"}} + ] +}} +Create 6-10 interconnected nodes exploring key business areas like Target Audience, Core Features, Marketing Strategy, Revenue Streams, etc. Ensure the IDs in connections match the nodes. +""" + response = LocalAIApi.create_response({ + "input": [ + {"role": "system", "content": "You are a helpful business strategy AI. You must respond in valid JSON matching the exact requested format."}, + {"role": "user", "content": prompt} + ], + "response_format": {"type": "json_object"} + }) + + if response.get("success"): + data = LocalAIApi.decode_json_from_response(response) + if not data: + return False + + # Parse and save to DB + node_map = {} + for n in data.get("nodes", []): + node = MindMapNode.objects.create( + project=project, + title=n.get("title", "Untitled"), + summary=n.get("summary", ""), + category=n.get("category", "General") + ) + node_map[n.get("id")] = node + + for c in data.get("connections", []): + source_id = c.get("source_id") + target_id = c.get("target_id") + if source_id in node_map and target_id in node_map: + MindMapConnection.objects.create( + project=project, + source=node_map[source_id], + target=node_map[target_id], + how=c.get("how", ""), + why=c.get("why", "") + ) + return True + return False diff --git a/core/ai_views.py b/core/ai_views.py index a7ae611..25cb272 100644 --- a/core/ai_views.py +++ b/core/ai_views.py @@ -1,18 +1,145 @@ 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': - data = json.loads(request.body) - user_message = data.get('message') + project = get_object_or_404(Project, pk=pk, user=request.user) - # In a real app, this would call your AI service (e.g., GPT, Gemini) - # Here we mock the AI response - ai_response = f"AI thinking about: {user_message}. Based on my analysis, you should consider..." - - return JsonResponse({'response': ai_response}) - return JsonResponse({'error': 'Invalid request'}, status=400) \ No newline at end of file + 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} + ], + "response_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) \ No newline at end of file diff --git a/core/templates/core/create_project.html b/core/templates/core/create_project.html index fa5065e..c8b77fc 100644 --- a/core/templates/core/create_project.html +++ b/core/templates/core/create_project.html @@ -1,22 +1,55 @@ {% extends 'base.html' %} + {% block content %} -
-

Create New Project

-
- {% csrf_token %} -
- - +
+
+
+
+
+

Start a New Project

+
+
+ + {% csrf_token %} +
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+ +
-
- - -
-
- - -
- - +
-{% endblock %} + + +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/project_detail.html b/core/templates/core/project_detail.html index 7cb049e..5ac7b58 100644 --- a/core/templates/core/project_detail.html +++ b/core/templates/core/project_detail.html @@ -1,49 +1,297 @@ {% extends 'base.html' %} {% block content %} -
-

{{ project.title }}

-

Industry: {{ project.industry }}

-

Goal: {{ project.goal }}

- -

Mind Map Nodes

-
    - {% for node in nodes %} -
  • {{ node.title }} - {{ node.category }}
  • - {% endfor %} -
- -

Connections

-
    - {% for conn in connections %} -
  • {{ conn.source.title }} -> {{ conn.target.title }} (Why: {{ conn.why }})
  • - {% endfor %} -
- -

Chat with AI

-
- - +
+
+
+

{{ project.title }}

+

{{ project.industry }} | Goal: {{ project.goal }}

+
+
-
-
Waiting for input...
+ +
+ +
+
+
+
Interactive Mind Map
+ +
+
+ +
+
+
+
+ + +
+ +
+
+
Node Details
+
+
+

Click on a node or edge in the map to see its details here.

+
+
+ + +
+
+
Ask AI Assistant
+
+
+
+ Hello! I'm your AI business strategist. Ask me to expand on a node, brainstorm new ideas, or analyze the map. If you ask me to add nodes to the map, I will! +
+
+ + +
+
+
+
+ + + + + + -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/views.py b/core/views.py index 9666f0c..51a3454 100644 --- a/core/views.py +++ b/core/views.py @@ -3,6 +3,7 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.forms import UserCreationForm from django.contrib.auth import login from .models import Project, MindMapNode, MindMapConnection +from .ai_helpers import generate_initial_mindmap def signup(request): if request.method == 'POST': @@ -20,7 +21,7 @@ def home(request): @login_required def project_list(request): - projects = Project.objects.filter(user=request.user) + projects = Project.objects.filter(user=request.user).order_by('-created_at') return render(request, 'core/project_list.html', {'projects': projects}) @login_required @@ -41,5 +42,7 @@ def create_project(request): industry = request.POST.get('industry') goal = request.POST.get('goal') project = Project.objects.create(user=request.user, title=title, industry=industry, goal=goal) + # Automatically generate the first mind map based on the input + generate_initial_mindmap(project) return redirect('project_detail', pk=project.pk) - return render(request, 'core/create_project.html') \ No newline at end of file + return render(request, 'core/create_project.html')