summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2026-06-20 18:38:59 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2026-06-20 18:38:59 +0100
commite42487bbe44eaebfd68114e28f74f17f62d4603d (patch)
tree8cc9fb8d64b9661b45ee24973ff37de05cf2e131
parentRemove more of the tag based image selection (diff)
downloadtoy-e42487bbe44eaebfd68114e28f74f17f62d4603d.zip
Replace the remaining legacy image stuff
Generalises the textMatcher for any purpose, including its original use of selecting an image for some text. Then replaces the legacy outfit selector with an LLM based one using it.
-rw-r--r--scripts/toy.groovy20
-rw-r--r--scripts/toy/debug.groovy1
-rw-r--r--scripts/toy/imagery.groovy94
-rw-r--r--scripts/toy/intro.groovy2
-rw-r--r--scripts/toy/llmSession.groovy5
5 files changed, 48 insertions, 74 deletions
diff --git a/scripts/toy.groovy b/scripts/toy.groovy
index a19b024..b89f6b0 100644
--- a/scripts/toy.groovy
+++ b/scripts/toy.groovy
@@ -402,6 +402,26 @@ ${jspec.collect{ k, v -> "\"${k}\": ${v}" }.join(",\n ")}
}
}
+ final textMatcher = { text, dommePrompt, assetDetail, assetType, requestPrompt, assets ->
+ final assetList = assets.collect{ id, value -> "* ${id} : ${value}" }.join("\n");
+ final idList = assets.keySet().collect{ id -> "\"${id}\"" }.join(" | ");
+ final request = [
+ model: loadString("toy.chatModel"),
+ grammar: "root ::= ( ${idList} )",
+ messages: [
+ [role: "system", content: """
+You are ${DOMME.title} ${DOMME.fullName}.
+${dommePrompt}
+Given a list of ${assetDetail}, you select the ${assetType} which best ${requestPrompt}.
+The available ${assetType}s are:
+${assetList}"""
+ ],
+ [role: "user", content: "Write only an ${assetType} Id from the list as an unformatted string, select an ${assetType} for the text: ${text}"]
+ ]
+ ];
+ return callAPI("chat/completions", request)?.choices?[0]?.message?.content;
+ }
+
final chatInitialMessages = { String ... extra ->
def messages = [
[role: "system", content: """
diff --git a/scripts/toy/debug.groovy b/scripts/toy/debug.groovy
index d5afbeb..5283411 100644
--- a/scripts/toy/debug.groovy
+++ b/scripts/toy/debug.groovy
@@ -5,7 +5,6 @@
toy.metaClass.playActivity {
final keys = new ArrayList<?>(activityList.keySet());
final act = getSelectedValue("Choose activity", keys);
- dress(activityList[keys[act]].images);
activityList[keys[act]].func();
};
diff --git a/scripts/toy/imagery.groovy b/scripts/toy/imagery.groovy
index 23b4c79..7e91c00 100644
--- a/scripts/toy/imagery.groovy
+++ b/scripts/toy/imagery.groovy
@@ -1,90 +1,44 @@
{ toy ->
- final IMAGEDATA = {
- return new File("${toy.DATAFOLDER}/images/toy")
- .listFiles()
- .findAll { d -> d.isDirectory(); }
- .collectEntries { d -> [
- d.getName(), [
- domme: d.getName(),
- sets: d.listFiles()
- .findAll { s -> s.isDirectory(); }
- .collect { s -> [ sub: s, f: new File(s.getPath() + "/set.json")] }
- .findAll { s -> s.f.canRead() }
- .collectEntries { s -> [
- s.sub.getName(), [
- set: s.sub.getName(),
- images: new groovy.json.JsonSlurper().parse(s.f).captions
- ]
- ]}
- ]
- ]
- };
- }();
-
toy.metaClass.showImageForText { text ->
- final captionList = DOMME.captions.collect{ id, caption -> "* ${id} : ${caption}" }.join("\n");
- final request = [
- model: loadString("toy.chatModel"),
- grammar: "root ::= ( ${DOMME.captions.keySet().collect{ id -> "\"${id}\"" }.join(" | ")} )",
- messages: [
- [role: "system", content: """
-You are ${DOMME.title} ${DOMME.fullName}.
-${DOMME.prompt}
-${DOMME.promptSet?:""}
-Given a list of images with captions and some spoken words, you select the image which best captures the sentiments of the words.
-The available images are:
-${captionList}"""
- ],
- [role: "user", content: "Write only an image Id from the list as an unformatted string, select an image for the text: ${text}"]
- ]
- ];
- while (true) {
- final imageName = callAPI("chat/completions", request)?.choices?[0]?.message?.content;
- if (DOMME.captions[imageName]) {
- setImage("toy/${DOMME.id}/${DOMME.set}/${imageName}.jpg");
- return imageName;
- }
- showPopup("Given:\n${requestMessages}\n\nGot: ${imageName}");
- }
- return null;
+ final imageName = textMatcher(text, "${DOMME.prompt} ${DOMME.promptSet?:""}", "images with captions and some spoken words",
+ "image", "captures the sentiments of the words", DOMME.captions);
+ setImage("toy/${DOMME.id}/${DOMME.set}/${imageName}.jpg");
+ return imageName;
}
toy.metaClass.showLounge {
setImage("toy/$OWNER/lounge.jpg");
};
- toy.metaClass.selectImageSet { domme, specs ->
- def matches = IMAGEDATA[domme]?.sets.values();
- if (!matches) return null;
- return matches[getRandom(matches.size())];
+ toy.metaClass.selectImageSet { activity ->
+ return textMatcher(activity, DOMME.prompt, "clothing descriptions and settings",
+ "ensemble", "best suits the activity", new File("${toy.DATAFOLDER}/images/toy/${DOMME.id}")
+ .listFiles()
+ .findAll { d -> d.isDirectory() }
+ .collectEntries { d -> [ d.getName(), new File("${toy.DATAFOLDER}/images/toy/${DOMME.id}/${d.getName()}/set.json") ] }
+ .findAll { d, s -> s.canRead() }
+ .collectEntries { d, s -> [ d, new groovy.json.JsonSlurper().parse(s).promptSet ] } );
};
- toy.metaClass.dress { specs, onChange = {} ->
+ toy.metaClass.dress { activity, onChange = {} ->
def outfit = loadString("toy.owner.outfit");
def outfitTime = loadInteger("toy.owner.outfitTime");
- if (outfit && outfitTime > getTime() - 7200) { // Recent, check
- def prev = IMAGEDATA[DOMME.id];
- if (prev) {
- prev = prev.sets[outfit];
- if (prev) {
- return outfit;
- }
- }
- }
- onChange();
- outfit = selectImageSet(DOMME.id, specs);
- if (!outfit) {
- showPopup("No outfit for ${DOMME.id} : $specs");
+ final newOutfit = selectImageSet("${activity}${(outfit && outfitTime > getTime() - 7200) ? ". Select ${outfit} if it is suitable" : ""}");
+ if (!newOutfit) {
+ showPopup("No outfit for ${DOMME.id} : $activity");
save("toy.owner.outfit", null);
save("toy.owner.outfitTime", null);
- loadDomme(DOMME.id, outfit);
+ loadDomme(DOMME.id, null);
return null;
}
- save("toy.owner.outfit", outfit.set);
+ if (newOutfit != outfit) {
+ onChange();
+ }
+ save("toy.owner.outfit", newOutfit);
save("toy.owner.outfitTime", getTime());
- loadDomme(DOMME.id, outfit.set);
- executeTrigger("ownerOutfitChange", outfit.set);
- return outfit.set;
+ loadDomme(DOMME.id, newOutfit);
+ executeTrigger("ownerOutfitChange", newOutfit);
+ return newOutfit;
};
return [:];
}
diff --git a/scripts/toy/intro.groovy b/scripts/toy/intro.groovy
index 2ad544c..097d422 100644
--- a/scripts/toy/intro.groovy
+++ b/scripts/toy/intro.groovy
@@ -9,7 +9,7 @@
showButton("OK");
// Welcome
- dress([]);
+ dress("Introducing yourself");
llmPresent("...");
def messages = chatInitialMessages();
diff --git a/scripts/toy/llmSession.groovy b/scripts/toy/llmSession.groovy
index 2689cdf..9445153 100644
--- a/scripts/toy/llmSession.groovy
+++ b/scripts/toy/llmSession.groovy
@@ -27,8 +27,9 @@
playSchedule();
return;
}
- dress([]);
- def messages = chatInitialMessages("You want to play with ${loadString("intro.name")}. You can dismiss ${loadString("intro.name")} when you wish by writing \"DISMISSED.\" only.");
+ final activity = "You want to play with ${loadString("intro.name")}.";
+ dress(activity);
+ def messages = chatInitialMessages("${activity} You can dismiss ${loadString("intro.name")} when you wish by writing \"DISMISSED.\" only.");
if (summon(messages)) {
while (!messages.last().content.toUpperCase().startsWith("DISMISSED") && !messages.last().content.toUpperCase().contains("YOU ARE DISMISSED")) {
llmPresent(messages.last().content);