GatherContextCommand.cs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. using UnityEditor;
  2. using UnityEngine;
  3. using LLM.Editor.Data;
  4. using LLM.Editor.Helper;
  5. using LLM.Editor.Analysis;
  6. using JetBrains.Annotations;
  7. using System.Collections.Generic;
  8. namespace LLM.Editor.Commands
  9. {
  10. /// <summary>
  11. /// The master "tool" command. It receives a batch of data requests from the LLM,
  12. /// dispatches them to the appropriate Context Providers, and aggregates the results.
  13. /// </summary>
  14. [UsedImplicitly]
  15. public class GatherContextCommand : ICommand
  16. {
  17. private readonly GatherContextParams _params;
  18. public GatherContextCommand(string jsonParams)
  19. {
  20. _params = jsonParams?.FromJson<GatherContextParams>();
  21. }
  22. public CommandOutcome Execute(CommandContext context)
  23. {
  24. if (_params?.requests == null || _params.requests.Count == 0)
  25. {
  26. Debug.LogError("[GatherContextCommand] No requests found in parameters.");
  27. return CommandOutcome.Error;
  28. }
  29. var results = new Dictionary<string, object>();
  30. foreach (var request in _params.requests)
  31. {
  32. var targetObject = ResolveIdentifier(request.subjectIdentifier);
  33. var provider = ContextProviderRegistry.GetProvider(request.dataType);
  34. if (provider == null)
  35. {
  36. Debug.LogWarning($"[GatherContextCommand] No context provider found for dataType '{request.dataType}'. Skipping request for key '{request.contextKey}'.");
  37. results[request.contextKey] = $"Error: No provider for dataType '{request.dataType}'";
  38. continue;
  39. }
  40. // The qualifier is now passed as a JToken, which can be either a string or a complex object.
  41. // The provider itself will handle the specific type it expects. For most providers, this
  42. // will just be a string. For GetDataFromPath, it will be a PathRequest object.
  43. var qualifierString = request.qualifier?.Type == Newtonsoft.Json.Linq.JTokenType.String
  44. ? request.qualifier.ToString()
  45. : request.qualifier?.ToString(Newtonsoft.Json.Formatting.None);
  46. var contextData = provider.GetContext(targetObject, qualifierString);
  47. results[request.contextKey] = contextData;
  48. }
  49. var aggregatedContextJson = results.ToJson(true);
  50. context.CurrentSubject = aggregatedContextJson;
  51. Debug.Log($"[GatherContextCommand] Successfully gathered context for {_params.requests.Count} request(s). Pausing for next API call.");
  52. Debug.Log($"Aggregated Context:\n{aggregatedContextJson}");
  53. return CommandOutcome.AwaitingNextTurn;
  54. }
  55. /// <summary>
  56. /// Resolves an identifier string to a Unity Object.
  57. /// The identifier can be an InstanceID, a GUID, or an asset path.
  58. /// </summary>
  59. private static Object ResolveIdentifier(string identifier)
  60. {
  61. if (string.IsNullOrEmpty(identifier)) return null;
  62. // Priority 1: Try parsing as an InstanceID for scene objects
  63. if (int.TryParse(identifier, out var instanceId))
  64. {
  65. var obj = EditorUtility.InstanceIDToObject(instanceId);
  66. if (obj) return obj;
  67. }
  68. // Priority 2: Try resolving as a GUID for project assets
  69. var path = AssetDatabase.GUIDToAssetPath(identifier);
  70. if (!string.IsNullOrEmpty(path))
  71. {
  72. return AssetDatabase.LoadAssetAtPath<Object>(path);
  73. }
  74. // Priority 3: Assume it's a direct asset path
  75. if (System.IO.File.Exists(identifier) || System.IO.Directory.Exists(identifier))
  76. {
  77. return AssetDatabase.LoadAssetAtPath<Object>(identifier);
  78. }
  79. Debug.LogWarning($"[GatherContextCommand] Could not resolve identifier '{identifier}' to a Unity Object.");
  80. return null;
  81. }
  82. }
  83. }