123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- using UnityEditor;
- using UnityEngine;
- using LLM.Editor.Core;
- using LLM.Editor.Client;
- using LLM.Editor.Commands;
- using LLM.Editor.Analysis;
- using System.Threading.Tasks;
- using System.Collections.Generic;
- namespace LLM.Editor.GUI
- {
- public class MCPWindow : EditorWindow
- {
- private string _promptText = "The grenade launcher should hit the target.";
- private Vector2 _scrollPos;
- private bool _isRequestInProgress;
-
- private readonly List<Object> _stagedContextObjects = new();
- private List<string> _requestedRoles = new();
- private bool _isAnalysisContextRequested;
- private ILlmApiClient _apiClient;
- [MenuItem("Tools/LLM/MCP Assistant")]
- public static void ShowWindow()
- {
- GetWindow<MCPWindow>("MCP Assistant");
- }
- private void OnEnable()
- {
- CommandExecutor.OnQueueUpdated += Repaint;
- RequestAnalysisContextCommand.OnAnalysisContextRequested += HandleAnalysisContextRequested;
- CommandExecutor.OnContextReadyForNextTurn += HandleContextReadyForNextTurn;
- _apiClient = ApiClientFactory.GetClient();
- }
- private void OnDisable()
- {
- CommandExecutor.OnQueueUpdated -= Repaint;
- RequestAnalysisContextCommand.OnAnalysisContextRequested -= HandleAnalysisContextRequested;
- CommandExecutor.OnContextReadyForNextTurn -= HandleContextReadyForNextTurn;
- }
- private void HandleAnalysisContextRequested(RequestAnalysisContextParams contextParams)
- {
- _isAnalysisContextRequested = true;
- _requestedRoles = new List<string>(contextParams.subjectRoles);
- // Ensure our staging list has enough slots for what was requested
- while (_stagedContextObjects.Count < _requestedRoles.Count)
- {
- _stagedContextObjects.Add(null);
- }
- Repaint();
- }
-
- private void HandleContextReadyForNextTurn(string detailedContext)
- {
- Debug.Log("[MCPWindow] Received detailed context. Sending follow-up to LLM.");
- _ = SendFollowUpAsync(detailedContext);
- }
- private void OnGUI()
- {
- EditorGUILayout.LabelField("LLM Co-Pilot", EditorStyles.boldLabel);
-
- DrawSessionManagement();
- EditorGUILayout.Space();
- UnityEngine.GUI.enabled = SessionManager.HasActiveSession();
-
- DrawContextStagingArea();
- EditorGUILayout.Space();
-
- EditorGUILayout.LabelField("Enter your request:");
- _promptText = EditorGUILayout.TextArea(_promptText, GUILayout.Height(60));
-
- DrawActionButton();
- EditorGUILayout.Space();
-
- DrawCommandQueue();
-
- UnityEngine.GUI.enabled = true;
- }
-
- private void DrawSessionManagement()
- {
- EditorGUILayout.BeginHorizontal();
- if (SessionManager.HasActiveSession())
- {
- EditorGUILayout.LabelField($"Session: {SessionManager.GetCurrentSessionId()}");
- if (GUILayout.Button("End Session"))
- {
- SessionManager.EndSession();
- _stagedContextObjects.Clear();
- _isAnalysisContextRequested = false;
- }
- }
- else
- {
- if (GUILayout.Button("Start New Session"))
- {
- SessionManager.StartNewSession();
- }
- }
- EditorGUILayout.EndHorizontal();
- }
-
- private void DrawContextStagingArea()
- {
- EditorGUILayout.LabelField("Context (Drag & Drop Assets Here)", EditorStyles.boldLabel);
-
- if (GUILayout.Button("Add Context Slot", GUILayout.Width(150)))
- {
- _stagedContextObjects.Add(null);
- if(_isAnalysisContextRequested) _requestedRoles.Add("Custom");
- }
- var indexToRemove = -1;
- for (var i = 0; i < _stagedContextObjects.Count; i++)
- {
- EditorGUILayout.BeginHorizontal();
- var label = (_isAnalysisContextRequested && i < _requestedRoles.Count) ? _requestedRoles[i] : $"Context {i + 1}";
- _stagedContextObjects[i] = EditorGUILayout.ObjectField(label, _stagedContextObjects[i], typeof(Object), true);
-
- if (GUILayout.Button("X", GUILayout.Width(20)))
- {
- indexToRemove = i;
- }
- EditorGUILayout.EndHorizontal();
- }
- if (indexToRemove == -1) return;
- _stagedContextObjects.RemoveAt(indexToRemove);
- if(_isAnalysisContextRequested && indexToRemove < _requestedRoles.Count)
- {
- _requestedRoles.RemoveAt(indexToRemove);
- }
- Repaint();
- }
-
- private void DrawActionButton()
- {
- UnityEngine.GUI.enabled = SessionManager.HasActiveSession() && !_isRequestInProgress;
- if (_isAnalysisContextRequested)
- {
- if (!GUILayout.Button("Provide Staged Context to LLM")) return;
- var contextSummary = ContextBuilder.BuildTier1Summary(_stagedContextObjects);
- var followUpMessage = "Here is the context you requested:\n" + contextSummary;
- _ = SendFollowUpAsync(followUpMessage);
- }
- else
- {
- if (GUILayout.Button(_isRequestInProgress ? "Waiting for response..." : "Send Prompt"))
- {
- _ = SendInitialPromptAsync();
- }
- }
- }
-
- private void DrawCommandQueue()
- {
- UnityEngine.GUI.enabled = !_isRequestInProgress;
- EditorGUILayout.LabelField("Pending Commands", EditorStyles.boldLabel);
- _scrollPos = EditorGUILayout.BeginScrollView(_scrollPos, EditorStyles.helpBox);
-
- if (CommandExecutor.HasPendingCommands())
- {
- var nextCommand = CommandExecutor.GetNextCommand();
- EditorGUILayout.LabelField("Next Up: " + nextCommand.commandName);
- if (GUILayout.Button("Execute Next Command"))
- {
- CommandExecutor.ExecuteNextCommand();
- }
- }
- else
- {
- EditorGUILayout.LabelField("No pending commands.");
- }
- EditorGUILayout.EndScrollView();
- }
-
- private async Task SendInitialPromptAsync()
- {
- _isRequestInProgress = true;
- Repaint();
-
- // Pass the staged objects to the API client
- await _apiClient.SendPrompt(_promptText, _stagedContextObjects);
- _isRequestInProgress = false;
- ClearStagingArea();
- Repaint();
- }
-
- private async Task SendFollowUpAsync(string followUpMessage)
- {
- _isRequestInProgress = true;
- Repaint();
- await _apiClient.SendFollowUp(followUpMessage);
- _isRequestInProgress = false;
-
- // Only clear the staging area if the follow-up was initiated by the user
- // providing context. This preserves the UI state otherwise.
- if (_isAnalysisContextRequested)
- {
- ClearStagingArea();
- }
- Repaint();
- }
-
- private void ClearStagingArea()
- {
- _isAnalysisContextRequested = false;
- _stagedContextObjects.Clear();
- _requestedRoles.Clear();
- }
- }
- }
|