From 33a9c385739e0c5dbaecf61700b2d406576cf1a3 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 12 Mar 2026 14:15:33 +0000 Subject: [PATCH] Auto commit: 2026-03-12T14:15:33.094Z --- ai/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 404 bytes ai/__pycache__/local_ai_api.cpython-311.pyc | Bin 0 -> 19874 bytes core/__pycache__/ai_helpers.cpython-311.pyc | Bin 0 -> 3116 bytes core/__pycache__/ai_views.cpython-311.pyc | Bin 1099 -> 6824 bytes core/__pycache__/views.cpython-311.pyc | Bin 3168 -> 3367 bytes core/ai_helpers.py | 60 ++++ core/ai_views.py | 143 ++++++++- core/templates/core/create_project.html | 69 +++-- core/templates/core/project_detail.html | 326 +++++++++++++++++--- core/views.py | 7 +- 10 files changed, 538 insertions(+), 67 deletions(-) create mode 100644 ai/__pycache__/__init__.cpython-311.pyc create mode 100644 ai/__pycache__/local_ai_api.cpython-311.pyc create mode 100644 core/__pycache__/ai_helpers.cpython-311.pyc create mode 100644 core/ai_helpers.py diff --git a/ai/__pycache__/__init__.cpython-311.pyc b/ai/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..977e624f2d444f33b142f693b1ed4de14374d8b4 GIT binary patch literal 404 zcma)(u}T9$5QcZ}E=EFvwcTQuCRf>s5Ku8hY^=OyS$8MN#=VzW(k3^A-D5@eu^C*k* zIE(WnOY&CMqC~}4q@UcMo`WgDR&0Wn>B4~((&=K#7TV32n*lpB!Wo=u$%Z3VT3oK! z#A41)=AxJ)lc-?uN&UWl(sV9R3NsuHOWo{A3&H`nP?cCz(6lYw)lj)+UqMjud4&ZJ zv;KN6`#VjWbA^)grfb45E;JXVwz1z}lYmyhj>j|Ec#`cPona1XeNz{%PGJeMc4*TD z+S{rW5>la4&UK+3=Yw*!-95@t)dr4j*UQ7{)uG2%OeuXM<24z-lk4^W+=gd-1LhZd AO8@`> literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c4653afa26dc6a50320084ce353e5d0c28d4d81e GIT binary patch literal 19874 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 literal 0 HcmV?d00001 diff --git a/core/__pycache__/ai_helpers.cpython-311.pyc b/core/__pycache__/ai_helpers.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..38e324a0d16533cb60a0dc4987f6d7dde5d4575b GIT binary patch literal 3116 zcmb7G-EZ4Q5?@Nx7iC+LzaqtnZ{zgRs#RpiZF;z%Enp-~>_hEb;{-^YD+GqzShOip zw_L`tO94SqpnAvwxu8Idw1@gBmqYqc+<$QYfmH~out0zS_tZBA=>dm6-7NJHJFb8p zO%HdLvopV${q2Z<9~@K=T(AG@pT=AQp??dddxbiK7c($;fOMowCaOp)l7#e-9yUXj z@JhHMugIVyT{a^t5k2xbT8ZlNGRjAP6wC7xS3a^e)tp|v(sD!}-EaBr*dwU-Uj)OJZa$J+C z7AxYjl&CDQGAx4`s);LbesM*u;+rs^wKbNRVQ|v04LmjSY(!lTZn!$@Uoy>|$3nV8j^uK5xukT%$OJhxE9 z!ma{5#ip zC)XU?Dz!NemJ!3Mx{RmV;T%$Gv!2HsSJU8#p68Ju7aj>1&f{0CV4w?&fZQDMc$ddK zc7a%gs%Bp1vCdhg!_bYW=wWmm1P>WHPwuH2PxRQv6RgWH9tKwNFiqWH&#ngbWpFmz zO|m4KB!r~m^GQH=qR{j1AU~8DlAomyeL3hyVB{kqqG({GnVj%ry_qrY?=$b-LL)ZWj7>gA6OqxUsa!Ml_ILA*)ceiU`*pdM8u5{o z$hAhY_f7-S(eZk`l^J`Gy`SBFeP_0jInm6V@KG$04@7BwYrxDISIZpg=VJUBPB5$ zi27VN(0U{LW;6R{ec|9lD!VZ>fK@o)fANgP_{Qv*} literal 0 HcmV?d00001 diff --git a/core/__pycache__/ai_views.cpython-311.pyc b/core/__pycache__/ai_views.cpython-311.pyc index f43e924c7e03307482519be26c5544c54510dc7a..6bb38c23fd73afd98c9abf0cacfd8b5eb20478e8 100644 GIT binary patch literal 6824 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 delta 748 zcmY*X%}*0S6rbIl{eY#chJ-Dt%EU`kB)ted7$ug(0|`NrdN5qb!c6EwyNffsgf>Mu za3IEmwFjE02`9wh!RST*0~0S>(*v1=#Kd^7@#e|dQZT%k_ujnse($|`lR29FW(>Yf zB>I6Mg?@BD$@Rgc^thfLO8^W@+JZ(DqGGlFY*g1VEP#ww|B#!VTbARD9zmfM!GFFA zu@7oswHE*YOKV*ueGtTm9<5$v3vA<_oK)qSd;lAgGPDh_d?*2MEclGJ6s**gSK>Nv zlr0#T=YCOKtwC41j4psn2o3A5gpK!ohoa^2MO!gP{~U+&4-1dDUM7q=E2KJ6`Zv{C^ei+mssTbOLG% zuO^sLA$CAV1BSKxv^o4;AC2_UQ)}pXwV8fB-m=ndE4>F#?9~3|%guwnmOa+C$Myy~ z2C!Y}Xf_%yp6xyJmz8QR9HpXblPxRPwsMh?J2ma59o@VgB_}=?BJ*m#W#-#vK7#o_ zqAkc64SQta7EOqA_eSpkO~&V8A^sY7%RUZEWQtxFX8aG>M&swm{V8*&yM9OmCz92X d`=x2A17i7G0rHjI8?j34raJI{bkP5mQA5_styqt9TF-eC1fFnS?8rAv%B_YIut^O zJOrMW?_kiy4k=+o)IU(?I%o*V5aFdmC+!xV`rg|FtB>Kwd%y4Z-uHdKd3%M8erqF} z%}5wmKfDbpTFWYsSFMp-qpGikR^6`JMDq1zsoLQih@DjRGt1IC>Noiz5!J5Nn|vvU zsT2RDy8Y~wF4)_;qg)UTZz?>f{a~Hhh%FNGBZ1 z6KVmk*Wp^~M=_79h#-_%FJhw1H|vxYahZeb$^hwuO|6eSg~!Sbc};=mN)CvglVcNH zd8FGRtOunJ&Cyv#vAXYiG05uBRGRV86RlZWptEt5r0)h{Oqoz8dXxE-xwQu@g(630 zya`r9h-8FvyA{^KSq{8JbOjn}$@*fCf40Zpk8azOJN6{3s735>L)|Amu$rEj;Yv1) zkfgjYWGDGn)KJ?sDx4TP5{Z1I;bvywIuoJC1t^YZ+emB;ZU#OA1E260nwv_GIoZ;Sq{7idRc+n z#$e54U7WKKlGGfHg_UfOZ_{m?g^euBRSE|&*6}(2xt>pad`{Rlx-(-)p5loYux~`x zcfGvUyRADry0fJ_P|KXw`Y?Ob$wMo%I-O*^z@4YzB4ts#GJiwjp|pP#AA#{ZUrLN0 z6mA~sm+DRben>B~^T=_dA|3G{MF`o2duA^@GSl$HobK9{&Taku!B^88GRTDk3E^i- Pk~@|M59A|4u%gm$_dvh! delta 799 zcmZWnOKTHR6ux&}naLzGNn_G6k1|OJX`pf8gBC3AyD*4uq)^g z+oyDmQiJ~zF3=b+h%53eWC-zX@%z#_&^i>rW+}jlbf>-LvNWhO7)b#1$Y(viUmj-` zj`ZrWUL8wSX7Pb^o-#g?u3vmeJq>76=@Lh9#4c^n0})#RX>jgCA5lKgl_2F*qZHezhEcGKPP)*aWY zH(IO&;WQtG7!-mfWN*OshNMfH;19&1cX0s1_V_!s$W1xUAFJ;bN9jsUmAADFP4Hc< z0Dt7QmdpArmXSV>P(i>Nuq&H?)8=-Zf{IW(0G=GHyeUn-N(Z6o;m zoKE;(W}NlRgh7_|>7Z-@IM_jF2+-qC!fmeT^C1hYS|!1+=)y{yb7IhUnr60r)8rA=aNe5&k)Rub<`=cNLu+ 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

-
- - +
+ -
-
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')