Code to Clean Self-Commands from LLM/GPT AI Output

The code that follows is an ai-api file for the animesh bots which includes a cleanup procedure called "botclean" that cleans up the self-commands so they don't end up in the output (or voice!).  basically you will see that in the procedure   speak(string t)     the first thing that is done is to send   output    to    botclean     via    output = botclean(output);  .  Can be nice to get rid of those gnarly-looking self-commands once you have them working - enjoy!

March 26 2025 update - if you have problems with your LLM not terminating self-commands with a "/" (seems endemic to DeepSeek) you can try adding space as a delimeter,
change def of DELIM below to 
DELIM = "/ ";




// programmed responses to >>> outputs of ai system
string MOOD = "";

// added for cleanup of bot commands
list CMDS = ["btmv", "btrt" ,"btwr", "btnm", "btmd"];
string DELIM = "/"; // could include other delimiters as required, add a space to the string if you have trouble with your LLM not putting "/" at the end of self commands (DeepSeek seems to do this)

// if not already in your code
string strReplace(string str, string search, string replace) 
{   return llDumpList2String(llParseStringKeepNulls((str = "") + str, [search], []), replace); }

// main call
string botclean(string t) // cleans up self-commands
{   integer debug = FALSE; 
    if(debug)llOwnerSay(t);
    integer n = llGetListLength(CMDS);
    integer i; integer j; integer k; integer l;
    for(i=0;i<n;i++)
    {   t = strReplace(t,"/"+llList2String(CMDS,i)," "+llList2String(CMDS,i));
    }
    t = " "+t+" "; // padding
    if(debug)llOwnerSay(t);
    for(i=0;i<n;i++)
    {   j = llSubStringIndex(t,llList2String(CMDS,i));
        if(j != -1)
        {   k = j + 5; // jump past command and delimiter
            while( ( k < llStringLength(t) )
                && ( llSubStringIndex(DELIM,llGetSubString(t,k,k)) == -1 ) ) 
            {   k++;    }
            if(j-1 < 0) t = llGetSubString(t+" ",k+1,-1);
            else if(k+1 >= llStringLength(t)) t = llGetSubString(t,0,j-1);
            else t = llGetSubString(t,0,j-1)+llGetSubString(t+" ",k+1,-1);
        }
        if(debug)llOwnerSay(t);
    }
    return llStringTrim(strReplace(t,"  "," "),STRING_TRIM);
}
// end of cleanup code



speak(string output) //allows redirection of all output
{   output = botclean(output); // above cleanup
    if(llSubStringIndex(output,"/me") != 0) 
    {   llMessageLinked(LINK_ROOT, -9998, output, NULL_KEY); // voice
        llSleep(5.);
        llMessageLinked(LINK_THIS,-1000,"botanim 1 Bento-express_speak",NULL_KEY); // for Bento sexbot
        llSay(0,output); // if you use Nanite Vox (next line) comment this out (put // in front of llSay)
        llSetTimerEvent(3.); // to close mouth
    }
    //      next line is the Nanite Vox interface
    // llMessageLinked(LINK_THIS,-9999,llGetSubString(output,0,255),NULL_KEY); // uncomment this line to send AI output to Nanite Vox system
    //      the 255 puts a 256 char limit on output sent to the Nanite Vox - long strings can lead to memory stack-heap collision but will
    //      truncate some long output lines.  If you want more Nanite control features via AI contact me in-world and let's do it! (musichero)
}


default
{
    state_entry()
    {
    }
    
    timer()
    {   // llStopObjectAnimation("Bento-express_speak"); // for Bento sexbot
        llMessageLinked(LINK_THIS,-1000,"bentostop",NULL_KEY); // force close mouth
        llSetTimerEvent(0.);    
    }

// this event is triggered by the API
    link_message(integer link, integer n, string c, key id)
    {   // llOwnerSay("API heard: "+(string)n+","+c+","+(string)id); // echoes all messages - this is useful for debugging!
// link is link number sending the message (probably same link)
// n is an integer, -1 means output, 0 means API should process, a number of other n values occur for various purposes
// string is text
// key is speaker (or NULL_KEY)
// other n codes:   998     commands sent to cortex
//                  -1000   commands sent to rlv controller
//                  100x    commands sent to ai system
//                  999     responses from ai system
//                  -5      commands sent to and from natural question system
//
        if(n == -1)
        {   speak(c); return;   }
        if(n != 0)
        {   return; }
        string args = "";
        list parts = llParseString2List(c,[">>>"],[]);
        c = llList2String(parts,0);
        if(llGetListLength(parts) > 1) args = llList2String(parts,1);
// below here, c is the command, args is what was in the input *other* than the command
// which you can use as data for whatever you are trying to do!
        if(c == "sayname")
        {   speak("My name is "+llGetDisplayName(llGetOwner())+".");
        }
        else if(c == "saydate")
        {   speak("Today is "+llGetDate());
        }
        else if(c == "saytime")
        {   string time = llList2String(llParseString2List(llGetTimestamp(),["T","."],[]),1);
            speak("The precise time is "+time+" GMT.");
        }
// allows API to respond to an unknown stimulus
        else if(c == "stimulusunknown")
        {//   llOwnerSay("there was no response found or the mood was wrong for response to '"+args+"'");
        }
        else if(c == "wrongmood")
        {//   llOwnerSay("there was a response found but the mood was wrong for '"+args+"'");
        }
// API - Users can modify the above OR add new commands here; there are executed by the
//   >>>-flagged responses in the ai notecard, just follow the model 
/*
        else if(c == "anycommand")
        {   CODE TO EXECUTE WHEN    >>>anycommand    is the response on a line in the ai nc
        }
*/
// example (say "usercmd" to the bot):
        else if(c == "demo")
        {   
            speak("This demos the user-definable function");
        }
// example using args (try saying "userargs 123" to the bot)
        else if(c == "demoa")
        {
            speak("This demos a user-definable function with arguments: args = "+args);
        }
// mood change
        else if(llGetSubString(c,0,3) == "mood")
        {   string mood = llStringTrim(llGetSubString(c,4,-1),STRING_TRIM);
            // if(n == 0) llOwnerSay("API detected mood set to "+mood);
            // if(n == 1) llOwnerSay("API detected mood "+mood+" was reinforced");
            MOOD = mood; // save as global, reflects last reported mood
        }
    }
// one can also directly set the mood, using
//    llMessageLinked(LINK_SET,0,"setmood aroused",key);
// default mood is set using "setmood default"; key tells AI a party responsible for changing the mood
}



UPDATE January 17, 2025 - if you want to limit how far the bot attempts to move using a btmv command (kind of useful) note that you can just use the small modification in gpt or llmconnect-ctrl script, around line 78 or so, in the command(string t) procedure

// btmv --> moveto
    i = llSubStringIndex(t,"btmv");
    if(i != -1)
    {   l = llParseString2List(llStringTrim(llGetSubString(t,i+4,i+84),STRING_TRIM),[" ","<",">",",","/"],[]);
        v.x = llList2Float(l,0); v.y = llList2Float(l,1); v.z = llList2Float(l,2); 
        if(v.x != 0. && v.y != 0. && v.z != 0.
        && llVecDist(v,llGetPos()) < 100) /// distance limit
        {   llMessageLinked(LINK_THIS,-2000,"moveto "+(string)v,NULL_KEY); // move using controller command
        }
    } 

The critical line is the one with   
  llVecDist(v,llGetPos()) < 100  
where 100 is the distance limit for a move command.  Keeps bots from getting lost by the llm trying to move crazy distances!


Comments

Popular posts from this blog

Play With The Bots Over At The West Mill Store!

Welcome to the AI Animesh Sexbot Party @ Sunset Forest!

TTS2 - New Text to Audio Speech System! Hear What You Type In Media Audio!