File size: 6,336 Bytes
dec5552 34bec90 dec5552 34bec90 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | // Plays a list of commands through the agent and writes a report: for testing your scene and for demos.
// one command per line; "// ..." comments; "#answer <item_id>" answers the pending question; "#look <item_id>|none"
// sets the gaze; "#wait <seconds>" pauses (not counted in any step's time).
// In the Editor: assign a TextAsset and press Play. In a built game or batch run:
// Game.exe -sceneAgentScript commands.txt [-sceneAgentOut <dir>] [-sceneAgentShots] (quits when done)
// Report: <out>/report.json with every step's calls, results, answer, error, seconds and the scene signature.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using UnityEngine;
namespace SceneAgent
{
[RequireComponent(typeof(SceneAgentSession))]
public class SceneAgentScriptRunner : MonoBehaviour
{
public TextAsset script;
[Tooltip("Output folder (relative = under Application.persistentDataPath). Empty: SceneAgentRuns/<time>.")]
public string outputDirectory = "";
public bool screenshots;
public bool quitWhenDone;
public float serverTimeoutSeconds = 240f;
public bool Done { get; private set; }
public string ReportPath { get; private set; }
static string Arg(string name)
{
var a = Environment.GetCommandLineArgs();
int i = Array.IndexOf(a, name);
return i >= 0 && i + 1 < a.Length ? a[i + 1] : null;
}
IEnumerator Start()
{
string[] lines;
string cli = Arg("-sceneAgentScript");
if (cli != null) { lines = File.ReadAllLines(cli); quitWhenDone = true; }
else if (script != null) lines = script.text.Replace("\r\n", "\n").Split('\n');
else yield break;
if (Environment.GetCommandLineArgs().Contains("-sceneAgentShots")) screenshots = true;
string outDir = Arg("-sceneAgentOut") ?? outputDirectory;
if (string.IsNullOrEmpty(outDir)) outDir = Path.Combine("SceneAgentRuns", DateTime.Now.ToString("yyyyMMdd_HHmmss"));
outDir = Path.GetFullPath(Path.IsPathRooted(outDir) ? outDir : Path.Combine(Application.persistentDataPath, outDir));
Directory.CreateDirectory(outDir);
var session = GetComponent<SceneAgentSession>();
// the script sets the gaze (#look); the gaze components come back when it ends, for whoever uses the scene next
var pausedGaze = FindObjectsOfType<SceneAgentGaze>().Where(g => g.enabled).ToList();
foreach (var g in pausedGaze) g.enabled = false;
var server = session.agent != null ? session.agent.server : null;
var steps = new List<object>();
float t0 = Time.realtimeSinceStartup;
if (server != null)
while (!server.Ready && !server.Failed && Time.realtimeSinceStartup - t0 < serverTimeoutSeconds) yield return null;
var head = new Dictionary<string, object> {
{ "server_mode", server != null ? server.mode.ToString() : "none (agent.endpoint)" },
{ "server_status", server != null ? server.status : session.agent.endpoint },
{ "server_seconds", Math.Round(Time.realtimeSinceStartup - t0, 1) }, { "unity", Application.unityVersion } };
int n = 0;
if (server == null || server.Ready)
{
foreach (var raw in lines)
{
string line = raw.Trim();
if (line.Length == 0 || line.StartsWith("//")) continue;
if (line.StartsWith("#wait "))
{
yield return new WaitForSecondsRealtime(float.Parse(line.Substring(6).Trim(), CultureInfo.InvariantCulture));
continue;
}
if (line.StartsWith("#look "))
{
string id = line.Substring(6).Trim();
session.world.gaze = id == "none" ? null : id;
continue;
}
if (line.StartsWith("#answer ")) yield return session.Answer(line.Substring(8).Trim());
else yield return session.Command(line);
var r = session.LastResult;
steps.Add(new Dictionary<string, object> {
{ "step", line }, { "calls", r.turns.SelectMany(t => t.calls).Cast<object>().ToList() },
{ "results", r.turns.SelectMany(t => t.results).Cast<object>().ToList() },
{ "check", r.turns.Select(t => (object)t.check).ToList() }, { "final_text", r.finalText },
{ "clarification", r.clarificationQuestion }, { "error", r.error },
{ "seconds", Math.Round(r.seconds, 2) }, { "model_calls", (double)r.turns.Count },
{ "room", session.world.playerRoom }, { "signature", session.world.Signature() } });
if (screenshots)
{
ScreenCapture.CaptureScreenshot(Path.Combine(outDir, $"step_{n:00}.png"));
yield return new WaitForEndOfFrame();
yield return null;
}
n++;
}
}
var secs = steps.Select(s => (double)((Dictionary<string, object>)s)["seconds"]).OrderBy(x => x).ToList();
head["steps"] = (double)steps.Count;
head["errors"] = (double)steps.Count(s => ((Dictionary<string, object>)s)["error"] != null);
head["seconds_p50"] = secs.Count > 0 ? secs[secs.Count / 2] : 0.0;
head["results"] = steps;
ReportPath = Path.Combine(outDir, "report.json");
File.WriteAllText(ReportPath, MiniJson.Serialize(head));
Debug.Log($"[SceneAgent] script: {steps.Count} steps, report {ReportPath}");
foreach (var g in pausedGaze) if (g != null) g.enabled = true;
Done = true;
if (quitWhenDone) Application.Quit();
}
}
}
|