chore(unity): 固定Unity CLI Loop本地包并移除UnitySkills依赖
This commit is contained in:
@@ -0,0 +1,426 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// Translation result from Translator before formatting
|
||||
/// </summary>
|
||||
public class TranslationOutput
|
||||
{
|
||||
public string FriendlyMessage { get; set; } = "";
|
||||
public string Explanation { get; set; } = "";
|
||||
public string Example { get; set; } = "";
|
||||
public List<string> Solutions { get; set; } = new();
|
||||
public List<string> LearningTips { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translate raw errors/exceptions into structured data (no severity weighting)
|
||||
/// </summary>
|
||||
public interface IErrorTranslator
|
||||
{
|
||||
TranslationOutput TranslateFromException(Exception exception);
|
||||
TranslationOutput TranslateFromMessage(string errorMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format translation into DTO with severity weighting and final output shape
|
||||
/// </summary>
|
||||
public interface IErrorFormatter
|
||||
{
|
||||
UserFriendlyErrorDto Format(TranslationOutput translation, string originalError, Exception exception = null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default translator using pattern dictionary and simple fallbacks
|
||||
/// </summary>
|
||||
public class DefaultErrorTranslator : IErrorTranslator
|
||||
{
|
||||
private readonly ErrorTranslationDictionary _dictionary = new();
|
||||
private readonly FriendlyMessageGenerator _messageGenerator = new();
|
||||
|
||||
public TranslationOutput TranslateFromException(Exception exception)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
return new TranslationOutput
|
||||
{
|
||||
FriendlyMessage = "Internal error",
|
||||
Explanation = string.Empty,
|
||||
Example = string.Empty,
|
||||
Solutions = new List<string>(),
|
||||
LearningTips = new List<string>()
|
||||
};
|
||||
}
|
||||
|
||||
if (exception is McpSecurityException securityException)
|
||||
{
|
||||
return new TranslationOutput
|
||||
{
|
||||
FriendlyMessage = "Tool blocked by security settings",
|
||||
Explanation = securityException.SecurityReason ?? string.Empty,
|
||||
Solutions = new List<string>
|
||||
{
|
||||
"Open uLoopMCP Security Settings and enable the required permission"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (exception is TimeoutException)
|
||||
{
|
||||
return new TranslationOutput
|
||||
{
|
||||
FriendlyMessage = "Request timeout",
|
||||
Explanation = exception.Message,
|
||||
Solutions = new List<string>
|
||||
{
|
||||
"Increase timeout or optimize the operation to complete faster"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (exception is ToolDisabledException disabledException)
|
||||
{
|
||||
return new TranslationOutput
|
||||
{
|
||||
FriendlyMessage = $"Tool '{disabledException.ToolName}' is disabled. To enable it, go to {McpUIConstants.TOOL_SETTINGS_MENU_PATH}",
|
||||
Explanation = "This tool has been disabled in project settings.",
|
||||
Solutions = new List<string>
|
||||
{
|
||||
$"Enable '{disabledException.ToolName}' in {McpUIConstants.TOOL_SETTINGS_MENU_PATH}"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (exception is ParameterValidationException)
|
||||
{
|
||||
return new TranslationOutput
|
||||
{
|
||||
FriendlyMessage = exception.Message,
|
||||
Explanation = string.Empty,
|
||||
Solutions = new List<string>
|
||||
{
|
||||
"Fix parameter types/values according to the tool schema"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return new TranslationOutput
|
||||
{
|
||||
FriendlyMessage = "Internal error",
|
||||
Explanation = exception.Message
|
||||
};
|
||||
}
|
||||
|
||||
public TranslationOutput TranslateFromMessage(string errorMessage)
|
||||
{
|
||||
ErrorPattern pattern = _dictionary.FindPattern(errorMessage);
|
||||
|
||||
string friendly = pattern?.FriendlyMessage ?? _messageGenerator.TranslateError(errorMessage);
|
||||
string explanation = pattern?.Explanation ?? string.Empty;
|
||||
string example = pattern?.Example ?? string.Empty;
|
||||
List<string> solutions = pattern?.Solutions ?? new List<string>();
|
||||
List<string> tips = GetLearningTips(errorMessage);
|
||||
|
||||
return new TranslationOutput
|
||||
{
|
||||
FriendlyMessage = friendly,
|
||||
Explanation = explanation,
|
||||
Example = example,
|
||||
Solutions = solutions,
|
||||
LearningTips = tips
|
||||
};
|
||||
}
|
||||
|
||||
private List<string> GetLearningTips(string errorMessage)
|
||||
{
|
||||
List<string> tips = new();
|
||||
|
||||
if (errorMessage.Contains("Top-level statements"))
|
||||
{
|
||||
tips.Add("In Dynamic Tool, you can write code directly");
|
||||
tips.Add("Class and namespace declarations are not required");
|
||||
}
|
||||
|
||||
if (errorMessage.Contains("not all code paths return"))
|
||||
{
|
||||
tips.Add("Add a return statement at the end of the code");
|
||||
tips.Add("Return the execution result as a string");
|
||||
}
|
||||
|
||||
if (errorMessage.Contains("ambiguous reference"))
|
||||
{
|
||||
tips.Add("Clearly distinguish between UnityEngine.Object and System.Object");
|
||||
tips.Add("Using the full name (UnityEngine.Object) is safer");
|
||||
}
|
||||
|
||||
return tips;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default formatter applying severity weighting and assembling the DTO
|
||||
/// </summary>
|
||||
public class DefaultErrorFormatter : IErrorFormatter
|
||||
{
|
||||
public UserFriendlyErrorDto Format(TranslationOutput translation, string originalError, Exception exception = null)
|
||||
{
|
||||
return new UserFriendlyErrorDto
|
||||
{
|
||||
OriginalError = originalError ?? string.Empty,
|
||||
FriendlyMessage = translation?.FriendlyMessage ?? string.Empty,
|
||||
Explanation = translation?.Explanation ?? string.Empty,
|
||||
Example = translation?.Example ?? string.Empty,
|
||||
SuggestedSolutions = translation?.Solutions ?? new List<string>(),
|
||||
LearningTips = translation?.LearningTips ?? new List<string>(),
|
||||
Severity = DetermineSeverity(originalError, exception)
|
||||
};
|
||||
}
|
||||
|
||||
private ErrorSeverity DetermineSeverity(string errorMessage, Exception exception)
|
||||
{
|
||||
if (exception != null)
|
||||
{
|
||||
if (exception is McpSecurityException)
|
||||
{
|
||||
return ErrorSeverity.High;
|
||||
}
|
||||
if (exception is TimeoutException)
|
||||
{
|
||||
return ErrorSeverity.Medium;
|
||||
}
|
||||
if (exception is ToolDisabledException)
|
||||
{
|
||||
return ErrorSeverity.Medium;
|
||||
}
|
||||
if (exception is ParameterValidationException)
|
||||
{
|
||||
return ErrorSeverity.Low;
|
||||
}
|
||||
return ErrorSeverity.High;
|
||||
}
|
||||
|
||||
string msg = errorMessage ?? string.Empty;
|
||||
if (msg.Contains("Top-level statements") || msg.Contains("not all code paths"))
|
||||
{
|
||||
return ErrorSeverity.High;
|
||||
}
|
||||
if (msg.Contains("ambiguous reference"))
|
||||
{
|
||||
return ErrorSeverity.Medium;
|
||||
}
|
||||
return ErrorSeverity.Low;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Related Classes: FriendlyMessageGenerator, ErrorTranslationDictionary
|
||||
/// Now delegates to Translator and Formatter
|
||||
/// </summary>
|
||||
public class UserFriendlyErrorConverter
|
||||
{
|
||||
private readonly IErrorTranslator _translator;
|
||||
private readonly IErrorFormatter _formatter;
|
||||
|
||||
public UserFriendlyErrorConverter()
|
||||
{
|
||||
_translator = new DefaultErrorTranslator();
|
||||
_formatter = new DefaultErrorFormatter();
|
||||
}
|
||||
|
||||
public UserFriendlyErrorConverter(IErrorTranslator translator, IErrorFormatter formatter)
|
||||
{
|
||||
_translator = translator ?? new DefaultErrorTranslator();
|
||||
_formatter = formatter ?? new DefaultErrorFormatter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert an exception to an enhanced, user-friendly error response
|
||||
/// </summary>
|
||||
public UserFriendlyErrorDto ProcessException(Exception exception)
|
||||
{
|
||||
if (exception == null)
|
||||
{
|
||||
return new UserFriendlyErrorDto
|
||||
{
|
||||
OriginalError = string.Empty,
|
||||
FriendlyMessage = "Internal error",
|
||||
Explanation = string.Empty,
|
||||
Example = string.Empty,
|
||||
SuggestedSolutions = new List<string>(),
|
||||
LearningTips = new List<string>(),
|
||||
Severity = ErrorSeverity.High
|
||||
};
|
||||
}
|
||||
|
||||
TranslationOutput translation = _translator.TranslateFromException(exception);
|
||||
return _formatter.Format(translation, exception.Message, exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert execution result errors into a more understandable format
|
||||
/// </summary>
|
||||
public UserFriendlyErrorDto ProcessError(
|
||||
ExecutionResult originalResult,
|
||||
string originalCode)
|
||||
{
|
||||
string errorMessage = originalResult.ErrorMessage ?? string.Empty;
|
||||
if (originalResult.Logs?.Any() == true)
|
||||
{
|
||||
string combinedErrors = string.Join(" ", originalResult.Logs);
|
||||
if (!string.IsNullOrEmpty(combinedErrors))
|
||||
{
|
||||
errorMessage += " " + combinedErrors;
|
||||
}
|
||||
}
|
||||
|
||||
TranslationOutput translation = _translator.TranslateFromMessage(errorMessage);
|
||||
return _formatter.Format(translation, errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error Translation Dictionary - Translating Common Error Patterns into Understandable Messages
|
||||
/// </summary>
|
||||
public class ErrorTranslationDictionary
|
||||
{
|
||||
private static readonly Dictionary<string, ErrorPattern> Patterns = new()
|
||||
{
|
||||
["Top-level statements must precede"] = new ErrorPattern
|
||||
{
|
||||
PatternRegex = @"Top-level statements must precede",
|
||||
FriendlyMessage = "There is an issue with the code structure",
|
||||
Explanation = "In the Dynamic Tool, class and namespace declarations are not required. Write Unity API processing directly.",
|
||||
Example = @"AVOID: namespace Test { class MyClass { void Method() { ... } } }
|
||||
|
||||
CORRECT: GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
cube.transform.position = Vector3.zero;
|
||||
return ""Cube creation completed"";",
|
||||
Solutions = new List<string>
|
||||
{
|
||||
"Remove class definition and write only the code inside the method",
|
||||
"Remove namespace declaration",
|
||||
"Keep using statements and extract only the main processing"
|
||||
}
|
||||
},
|
||||
|
||||
["not all code paths return a value"] = new ErrorPattern
|
||||
{
|
||||
PatternRegex = @"not all code paths return",
|
||||
FriendlyMessage = "A return value is required at the end of the code",
|
||||
Explanation = "In the Dynamic Tool, you must return the execution result. Please add a return statement at the end of the processing.",
|
||||
Example = @"AVOID: GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
Debug.Log(""Completed"");
|
||||
|
||||
CORRECT: GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
Debug.Log(""Completed"");
|
||||
return ""Cube creation completed"";",
|
||||
Solutions = new List<string>
|
||||
{
|
||||
"Add 'return \"Execution completed\";' at the end of the code",
|
||||
"Add return statements to all paths of conditional branching",
|
||||
"Return the execution result as a string"
|
||||
}
|
||||
},
|
||||
|
||||
["Object' is an ambiguous reference"] = new ErrorPattern
|
||||
{
|
||||
PatternRegex = @"'Object' is an ambiguous reference",
|
||||
FriendlyMessage = "Object class reference is ambiguous",
|
||||
Explanation = "UnityEngine.Object and System.Object are mixed. Please specify explicitly.",
|
||||
Example = @"AVOID: Object.DestroyImmediate(obj);
|
||||
|
||||
CORRECT: UnityEngine.Object.DestroyImmediate(obj);",
|
||||
Solutions = new List<string>
|
||||
{
|
||||
"Explicitly specify UnityEngine.Object",
|
||||
"Use full name description"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public ErrorPattern FindPattern(string errorMessage)
|
||||
{
|
||||
foreach (var kvp in Patterns)
|
||||
{
|
||||
if (Regex.IsMatch(errorMessage, kvp.Value.PatternRegex))
|
||||
{
|
||||
return kvp.Value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User-Friendly Message Generation Feature
|
||||
/// </summary>
|
||||
public class FriendlyMessageGenerator
|
||||
{
|
||||
public string TranslateError(string originalError)
|
||||
{
|
||||
// Basic error translation logic
|
||||
if (originalError.Contains("Top-level statements"))
|
||||
{
|
||||
return "There is an issue with the code structure. Class and namespace definitions are not required.";
|
||||
}
|
||||
|
||||
if (originalError.Contains("not all code paths return"))
|
||||
{
|
||||
return "A return value is required at the end of the code. Please add 'return \"Completed\";'.";
|
||||
}
|
||||
|
||||
if (originalError.Contains("ambiguous reference"))
|
||||
{
|
||||
return "Class reference is ambiguous. Please explicitly write UnityEngine.Object.";
|
||||
}
|
||||
|
||||
if (originalError.Contains("Identifier expected"))
|
||||
{
|
||||
return "Syntax error. Please verify the code syntax.";
|
||||
}
|
||||
|
||||
return originalError; // Fallback
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error Pattern Definition
|
||||
/// </summary>
|
||||
public class ErrorPattern
|
||||
{
|
||||
public string PatternRegex { get; set; } = "";
|
||||
public string FriendlyMessage { get; set; } = "";
|
||||
public string Explanation { get; set; } = "";
|
||||
public string Example { get; set; } = "";
|
||||
public List<string> Solutions { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User-friendly error DTO for API/UI surfaces
|
||||
/// </summary>
|
||||
public class UserFriendlyErrorDto
|
||||
{
|
||||
public string OriginalError { get; set; } = "";
|
||||
public string FriendlyMessage { get; set; } = "";
|
||||
public string Explanation { get; set; } = "";
|
||||
public string Example { get; set; } = "";
|
||||
public List<string> SuggestedSolutions { get; set; } = new();
|
||||
public List<string> LearningTips { get; set; } = new();
|
||||
public ErrorSeverity Severity { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error Severity
|
||||
/// </summary>
|
||||
public enum ErrorSeverity
|
||||
{
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Critical
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c065fd707d94478f9e4f03843b0af04
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user