Working on implementing batch commands for UE wingman

This commit is contained in:
2026-04-17 05:51:13 -04:00
parent 6b057d1514
commit f19e8ccb72
5 changed files with 95 additions and 32 deletions

View File

@@ -19,10 +19,10 @@ class UWing_GraphNode_SearchTypes : public UWingHandler
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, meta=(Description="Query string, can contain * wildcards"))
FString Query;
UPROPERTY(EditAnywhere, meta=(Description="Array of query strings; each may contain * wildcards"))
FWingJsonArray Queries;
UPROPERTY(EditAnywhere, meta=(Optional, Description="Maximum number of results (default 50)"))
UPROPERTY(EditAnywhere, meta=(Optional, Description="Maximum number of results per query (default 50)"))
int32 MaxResults = 50;
UPROPERTY(EditAnywhere, meta=(Description="Target graph"))
@@ -40,20 +40,38 @@ public:
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
if (!TargetGraph) return;
FWingGraphActions GraphActions(TargetGraph);
TArray<FWingGraphAction*> Results = GraphActions.Search(Query, MaxResults, false);
for (const FWingGraphAction* Action : Results)
// Validate all entries are strings before running any searches.
TArray<FString> QueryStrings;
QueryStrings.Reserve(Queries.Array.Num());
for (const TSharedPtr<FJsonValue>& QueryVal : Queries.Array)
{
WingOut::Stdout.Printf(TEXT("%s\n"), *Action->Name);
FString QueryStr;
if (!QueryVal->TryGetString(QueryStr))
{
WingOut::Stdout.Print(TEXT("ERROR: Queries must be an array of strings.\n"));
return;
}
QueryStrings.Add(QueryStr);
}
if (Results.Num() == 0)
FWingGraphActions GraphActions(TargetGraph);
for (const FString& Query : QueryStrings)
{
WingOut::Stdout.Print(TEXT("No matching node types found.\n"));
}
else if (Results.Num() >= MaxResults)
{
WingOut::Stdout.Printf(TEXT("WARNING: Reached limit of %d results. You may specify MaxResults.\n"), MaxResults);
WingOut::Stdout.Printf(TEXT("\n=== %s ===\n\n"), *Query);
TArray<FWingGraphAction*> Results = GraphActions.Search(Query, MaxResults, false);
for (const FWingGraphAction* Action : Results)
{
WingOut::Stdout.Printf(TEXT("%s\n"), *Action->Name);
}
if (Results.Num() == 0)
{
WingOut::Stdout.Print(TEXT("No matching node types found.\n"));
}
else if (Results.Num() >= MaxResults)
{
WingOut::Stdout.Printf(TEXT("WARNING: Reached limit of %d results. You may specify MaxResults.\n"), MaxResults);
}
}
}
};

View File

@@ -17,10 +17,10 @@ class UWing_Widget_SearchTypes : public UWingHandler
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, meta=(Description="Query string, can contain *"))
FString Query;
UPROPERTY(EditAnywhere, meta=(Description="Array of query strings; each may contain *"))
FWingJsonArray Queries;
UPROPERTY(EditAnywhere, meta=(Optional, Description="Maximum number of results (default 50)"))
UPROPERTY(EditAnywhere, meta=(Optional, Description="Maximum number of results per query (default 50)"))
int32 MaxResults = 50;
virtual void Register() override
@@ -31,20 +31,38 @@ public:
}
virtual void Handle() override
{
WingWidgets Widgets;
TArray<WingWidgets::Type> Results = Widgets.Search(Query, MaxResults, false);
for (const WingWidgets::Type& Entry : Results)
// Validate all entries are strings before running any searches.
TArray<FString> QueryStrings;
QueryStrings.Reserve(Queries.Array.Num());
for (const TSharedPtr<FJsonValue>& QueryVal : Queries.Array)
{
WingOut::Stdout.Printf(TEXT("%s\n"), *Entry.MenuName);
FString QueryStr;
if (!QueryVal->TryGetString(QueryStr))
{
WingOut::Stdout.Print(TEXT("ERROR: Queries must be an array of strings.\n"));
return;
}
QueryStrings.Add(QueryStr);
}
if (Results.Num() == 0)
WingWidgets Widgets;
for (const FString& Query : QueryStrings)
{
WingOut::Stdout.Print(TEXT("No matching widget types found.\n"));
}
else if (Results.Num() >= MaxResults)
{
WingOut::Stdout.Printf(TEXT("WARNING: Reached limit of %d results. You may specify MaxResults.\n"), MaxResults);
WingOut::Stdout.Printf(TEXT("\n=== %s ===\n\n"), *Query);
TArray<WingWidgets::Type> Results = Widgets.Search(Query, MaxResults, false);
for (const WingWidgets::Type& Entry : Results)
{
WingOut::Stdout.Printf(TEXT("%s\n"), *Entry.MenuName);
}
if (Results.Num() == 0)
{
WingOut::Stdout.Print(TEXT("No matching widget types found.\n"));
}
else if (Results.Num() >= MaxResults)
{
WingOut::Stdout.Printf(TEXT("WARNING: Reached limit of %d results. You may specify MaxResults.\n"), MaxResults);
}
}
}
};

View File

@@ -206,7 +206,19 @@ FString UWingServer::HandleRequest(const FString& Line)
{
if (Result[i] == TEXT('\0')) Result[i] = TEXT(' ');
}
return Result;
// Wrap the text in an MCP content-block array: [{"type":"text","text":"..."}]
TSharedPtr<FJsonObject> Block = MakeShared<FJsonObject>();
Block->SetStringField(TEXT("type"), TEXT("text"));
Block->SetStringField(TEXT("text"), Result);
TArray<TSharedPtr<FJsonValue>> Blocks;
Blocks.Add(MakeShared<FJsonValueObject>(Block));
FString OutJson;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutJson);
FJsonSerializer::Serialize(Blocks, Writer);
return OutJson;
}
void UWingServer::TryCallHandler(const FString &Line)

View File

@@ -128,11 +128,14 @@ def handle_message(msg):
arguments = params.get("arguments", {})
result = forward_to_editor(arguments)
if isinstance(result, dict) and "error" in result:
text = result["error"]
content = [{"type": "text", "text": result["error"]}]
else:
text = result
try:
content = json.loads(result)
except json.JSONDecodeError:
content = [{"type": "text", "text": "Malformed response from editor."}]
return make_jsonrpc(msg_id, {
"content": [{"type": "text", "text": text}],
"content": content,
})
return {

View File

@@ -57,9 +57,21 @@ def main():
try:
parsed = json.loads(result)
print(json.dumps(parsed, indent=2))
except json.JSONDecodeError:
print(result)
print("Error: response is not valid JSON.")
sys.exit(1)
if not isinstance(parsed, list):
print("Error: response is not a list of content blocks.")
sys.exit(1)
for block in parsed:
if not (isinstance(block, dict)
and block.get("type") == "text"
and isinstance(block.get("text"), str)):
print("Error: response contains a non-text block.")
sys.exit(1)
print("\n---\n".join(block["text"] for block in parsed))
if __name__ == "__main__":
main()