Program-O / Program-O

This project has now been archived. Please visit https://github.com/theramenrobotdiscocode/lemur-engine for the newest AIML Php Mysql Chatbot.
http://www.program-o.com
GNU General Public License v3.0
14 stars 9 forks source link

<bot name="name"/>-tag may appear in pattern #442

Open Josef-Meier opened 6 years ago

Josef-Meier commented 6 years ago

Version 2.6.10 Hello, I have discovered some AIML files (in the header version 1.0) in which the bot tag is used not only in the template- but also in the pattern elements. Like this, because I am building a German-speaking bot: http://alicebot.wikidot.com/aiml:de-de:cdrossman:alice:german-2-aiml It was not easy for me to find something more detailed about the bot tag under the AIML 1.0 standard or a specification. So I'm not sure if this actually complies with the specification of AIML 1.0, as some say. Then I found a 10 year old forum entry, where the topic was treated: http://knytetrypper.proboards.com/thread/397/aiml-bot-name-pattern Here are some dead links referenced on the pages of alicebot.org. Here the bot-day should be described closer. Therefore, it should already be possible with the AIML 1.0 standard to use the <bot name = "name" /> in "pattern". And only this <bot name = "name" /> tag may be used in pattern. But then I found another link where the bot tags are explained: http://mctarek.free.fr/AIML/AIML_Tags.htm Here is a text extract of it:

 AIML 1.0 Tags Set
  Adopted by A.L.I.C.E. A.I. Foundation         
    AIML Architecture Committee         
    July 16, 2001

  Tag:                  Tag Type:                      Note:
  <bot name="name"/>    Built-in bot parameter         may appear in pattern
  <bot name="XXX"/>     Custom bot parameter

A look at the aiml.xsd also shows that basically two tags are provided for pattern. Here is an excerpt from the aiml.xsd:

...
        <xs:element name="pattern">
                <xs:complexType mixed="true">
                        <xs:sequence>
                                <xs:choice minOccurs="0" maxOccurs="unbounded">
                                        <xs:element ref="aiml:bot"/>
                                        <xs:element ref="aiml:eval"/>
                                </xs:choice>
                        </xs:sequence>
                </xs:complexType>
        </xs:element>
...

Maybe there is a problem with the eval tag too?

Here I have an AIML example: Note: The bot name is unimportant in this case and does not need to be written into "pattern" because the name is resolved via the bot-tag. Otherwise always you would have to adapt of foreign AIML files the bot name before upload.

<category>
   <pattern>DO YOU KNOW WHO * IS</pattern>
   <template>I don't know who <star/> is.</template>
</category>
<category>
   <pattern>DO YOU KNOW WHO <bot name="name"/> IS</pattern>
   <template>This is me. I am <bot name="name"/>.</template>
</category>

Two small changes may help. One in the function "unset_all_bad_pattern_matches()" and the other in function "find_aiml_matches()" of the file "find_aiml.php". Look at the comments with "//// Meier":

One:

...
function unset_all_bad_pattern_matches(&$convoArr, $allrows, $lookingfor)
{
    global $error_response;

    $lookingfor_lc = _strtolower($lookingfor);
    $current_topic = get_topic($convoArr);
    $current_topic_lc = _strtolower($current_topic);
    $current_thatpattern = (isset ($convoArr['that'][1][1])) ? $convoArr['that'][1][1] : '';

    //file_put_contents(_LOG_PATH_ . 'allrows.txt', print_r($allrows, true));
    if (!empty($current_thatpattern))
    {
        runDebug(__FILE__, __FUNCTION__, __LINE__, "Current THAT = $current_thatpattern", 1);
    }

    $default_pattern = $convoArr['conversation']['default_aiml_pattern'];
    $default_pattern_lc = _strtolower($default_pattern);
    $tmp_rows = array();
    $relevantRows = array();
    //if default pattern keep
    //if direct pattern match keep
    //if wildcard or direct pattern match and direct or wildcard thatpattern match keep
    //if wildcard pattern matches found aiml keep
    //the end......
    $tmp_count = number_format(count($allrows));
    runDebug(__FILE__, __FUNCTION__, __LINE__, "NEW FUNC Searching through {$tmp_count} rows to unset bad matches", 4);
    //runDebug(__FILE__, __FUNCTION__, __LINE__, 'The allrows array:' . print_r($allrows, true), 4);

    // If no pattern was found, exit early
    if (($allrows[0]['pattern'] == "no results") && (count($allrows) == 1))
    {
        $tmp_rows[0] = $allrows[0];
        $tmp_rows[0]['score'] = 1;
        runDebug(__FILE__, __FUNCTION__, __LINE__, "Returning error as no results where found", 1);

        return $tmp_rows;
    }

    //loop through the results array
    runDebug(__FILE__, __FUNCTION__, __LINE__, "Blue 5 to Blue leader. Starting my run now! Looking for '$lookingfor'", 4);
    $i = 0;

    foreach ($allrows as $all => $subrow)
    {
        //get the pattern
        $aiml_pattern = _strtolower($subrow['pattern']);
        if (stripos($aiml_pattern, '<bot') !== false)
        {
            $startPos = stripos($aiml_pattern, '<bot');
            $endPos   = stripos($aiml_pattern, '>');
            $len = $endPos - $startPos + 1;
            $botTag = substr($aiml_pattern, $startPos, $len);
            $btReplace = parse_bot_tag($convoArr, new SimpleXMLElement($botTag));
            $aiml_pattern = str_ireplace($botTag, $btReplace, $aiml_pattern);
            //// Meier: "<bot name="name"/>" may appear in pattern (#442)
            // http://mctarek.free.fr/AIML/AIML_Tags.htm
            // start new code
            runDebug(__FILE__, __FUNCTION__, __LINE__, "M: bot tag: '$botTag'  value: '$btReplace'", 4);
            runDebug(__FILE__, __FUNCTION__, __LINE__, "M: Replace bot tag in pattern with value from table botpersonality", 4);
            $subrow['pattern'] = $aiml_pattern;
            // end new code
        }
        $aiml_pattern_wildcards = build_wildcard_RegEx($aiml_pattern);

        //get the that pattern
...

Two:

...
function find_aiml_matches(&$convoArr)
{
    global $dbn, $error_response;
    $user_id = $convoArr['conversation']['user_id'];
    runDebug(__FILE__, __FUNCTION__, __LINE__, "Finding the aiml matches from the DB", 4);

    $i = 0;
    //TODO convert to get_it
    $bot_id = $convoArr['conversation']['bot_id'];
    $bot_parent_id = $convoArr['conversation']['bot_parent_id'];
    runDebug(__FILE__, __FUNCTION__, __LINE__, "Bot ID = $bot_id. Bot Parent ID = $bot_parent_id.", 4);
    // get bot and bot parent ID's

    $default_aiml_pattern = $convoArr['conversation']['default_aiml_pattern'];

    #$lookingfor = get_convo_var($convoArr,"aiml","lookingfor");
    $convoArr['aiml']['lookingfor'] = str_replace('  ', ' ', $convoArr['aiml']['lookingfor']);
    $lookingfor = trim(_strtoupper($convoArr['aiml']['lookingfor']));

    //get the stored topic
    $storedtopic = trim(_strtoupper(get_topic($convoArr)));
    runDebug(__FILE__, __FUNCTION__, __LINE__, "Stored topic = '$storedtopic'", 4);

    //get the cleaned previous bot response (that)
    $lastthat = (isset ($convoArr['that'][1][1])) ? $convoArr['that'][1][1] : '';
    $lastthat = rtrim(_strtoupper($lastthat));

    $params = array();
    if ($bot_parent_id != 0 && $bot_parent_id != $bot_id)
    {
        $sql_bot_like = "(bot_id = :bot_id OR bot_id = :bot_parent_id)";
        $params[':bot_id'] = $bot_id;
        $params[':bot_parent_id'] = $bot_parent_id;
    }
    else {
        $sql_bot_like = "bot_id = :bot_id";
        $params[':bot_id'] = $bot_id;
    }
    // Build the pattern search
    $rplTemplate = "'[search]' LIKE (REPLACE(REPLACE(`[field]`, '*', '%'), '_', '%'))";

    //// Meier: "<bot name="name"/>" may appear in pattern (#442)
    //   http://mctarek.free.fr/AIML/AIML_Tags.htm
    // change start
    $bot_name = $convoArr['bot_properties']['name'];
    $rplTemplateBotName = "'[search]' LIKE (REPLACE(REPLACE(REPLACE(`[field]`, '*', '%'), '_', '%'),'<BOT NAME=\"NAME\"/>', '${bot_name}'))";

    //$pattern_like = "\n        " . str_replace('[search]', $lookingfor, $rplTemplate);
    $pattern_like = "\n        " . str_replace('[search]', $lookingfor, $rplTemplateBotName);
    // change end
    $pattern_like = str_replace('[field]', 'pattern', $pattern_like);

    // Placeholders for thatpattern and topic, in case they're empty
    $thatpattern_like = '';
    $topic_like = '';

    // Build the thatpattern search
    if (!empty($lastthat))
    {
        $thatpattern_like = "\n        OR " . str_replace('[search]', $lastthat, $rplTemplate);
        $thatpattern_like = str_replace('[field]', 'thatpattern', $thatpattern_like);
    }

    // Build the topic search
    if (!empty($storedtopic))
    {
        $topic_like = "\n        OR " . str_replace('[search]', $storedtopic, $rplTemplate);
        $topic_like = str_replace('[field]', 'thatpattern', $topic_like);
    }

    // The SQL template - There will ALWAYS be a pattern search, but not necessarily a thatpattern or topic.
    // There will also ALWAYS be a search for the default response category
    $sql = <<<endSQL
SELECT `id`, `bot_id`, `pattern`, `thatpattern`, `topic`, `filename`, `template` FROM `$dbn`.`aiml` WHERE
    [sql_bot_like] AND ([pattern_like][thatpattern_like][topic_like]
        OR `pattern` LIKE '$default_aiml_pattern'
    )
    # ORDER BY `id` ASC, `topic` DESC, `pattern` ASC, `thatpattern` ASC;
endSQL;
...

The bot-tag is converted internally to uppercase in the "pattern". Need to change bot-tag to lowercase while download, otherwise you cannot upload the file later (look at aiml.xsd). The following change in the function getAIMLByFileName () in the file "download.php" could solve the problem:

...
        /** @noinspection SqlDialectInspection */
        $sql = "SELECT pattern, thatpattern, template FROM aiml WHERE topic LIKE :topic AND filename LIKE :cleanedFilename and bot_id = :bot_id;";
        $params = array(
            ':topic' => $topic,
            ':cleanedFilename' => $cleanedFilename,
            ':bot_id' => $bot_id
        );
        $result = db_fetchAll($sql, $params, __FILE__, __FUNCTION__, __LINE__);

        foreach ($result as $row)
        {
            $pattern = _strtoupper($row['pattern']);
            //// Meier <bot name="name"/>-tag may appear in pattern #442
            // set <BOT NAME="NAME"/> tag in pattern to lower (add only next line).
            $pattern =  str_replace("<BOT NAME=\"NAME\"/>","<bot name=\"name\"/>",$pattern);

            $template = str_replace("\r\n", '', $row['template']);
            $template = str_replace("\n", '', $row['template']);

            $newLine = str_replace('[pattern]', $pattern, $categoryTemplate);
            $newLine = str_replace('[template]', $template, $newLine);

            $that = (!empty ($row['thatpattern'])) ? '<that>' . $row['thatpattern'] .
                '</that>' : '';

            $newLine = str_replace('[that]', $that, $newLine);
            $fileContent .= "$newLine\n";
        }
...

Sorry for my bad English language skills.

Thanks

Josef-Meier commented 6 years ago

@OverlanAI No, should be different:

Josef-Meier commented 6 years ago

Hello Have worked out a few test cases for this topic. My exported test cases for the "-tag in pattern" topic :

<?xml version="1.0" encoding="UTF-8"?>
<!--  file: test_2.aiml                                                 -->
<aiml version="1.0">
  <category>
    <pattern>YOUR NAME IS *</pattern>
    <template>That's wrong. My name is <bot name="name"/>.</template>
  </category>
  <category>
    <pattern>YOUR NAME IS *</pattern>
    <that>* GUESS WHAT MY NAME IS</that>
    <template>Wrong guess. My name is <bot name="name"/>.</template>
  </category>
  <category>
    <pattern>
      <bot name="name"/>
    </pattern>
    <that>* GUESS WHAT MY NAME IS</that>
    <template>Yes. That's right.</template>
  </category>
  <category>
    <pattern>YOUR NAME IS <bot name="name"/></pattern>
    <that>* GUESS WHAT MY NAME IS</that>
    <template>That's right.</template>
  </category>
  <category>
    <pattern>HELLO MY NAME IS *</pattern>
    <template><think><set name="name"><star/></set></think>Nice to meet you, <get name="name"/>. Guess what my name is?</template>
  </category>
  <category>
    <pattern><bot name="name"/> IS A NICE NAME</pattern>
    <template>Nice, that you like the name <bot name="name"/>.</template>
  </category>
  <category>
    <pattern>DO YOU KNOW WHO JOE IS</pattern>
    <template>Joe is my neighbor.</template>
  </category>
  <category>
    <pattern>DO YOU KNOW WHO * IS</pattern>
    <template>I don't know who <star/> is.</template>
  </category>
  <category>
    <pattern>DO YOU KNOW WHO <bot name="name"/> IS</pattern>
    <template>This is me. I am <bot name="name"/>.</template>
  </category>
  <category>
    <pattern>
      <bot name="name"/>
    </pattern>
    <template>Yes. This is me. I am <bot name="name"/>.</template>
  </category>
</aiml>

Test Dialog:

Note: Bot name is "Chalmun". The canteen owner from Star Wars :-) And I copied the dialogue and inserted it here.

User say: Hello, my name is Peter. Bot say: Nice to meet you, Peter. Guess what my name is? User say: Your name is Carl. Bot say: Wrong guess. My name is Chalmun.

another visit in canteen :-):

User say: Hello, my name is Peter. Bot say: Nice to meet you, Peter. Guess what my name is? User say: Chalmun? Bot say: Yes. That's right.

User say: Do you know who Lando is? Bot say: I don't know who Lando is.

User Say: Do you know who Joe is? Bot say: Joe is my neighbor.

another visit in canteen :-):

User say: Hello, my name is Peter. Bot say: Nice to meet you, Peter. Guess what my name is? User say: Your name is Chalmun. Bot say: That's right.

User say: Chalmun Bot say: Yes. This is me. I am Chalmun.

User say: Chalmun is a nice name. Bot say: Nice, that you like the name Chalmun.

some other checks with default / emergency answers in the result: (they are here in german, because I have built a german bot)

User say: Chalmun bla bla bla Bot Say: ...Mit der Aussage kann ich im Moment nichts anfangen.

User say: bla bla bla Chalmun Bot say: ...Mir ist nicht ganz klar, was Sie mir damit sagen möchten.

User say: bla bla Chalmun bla bla Bot say: ...Das sagt mir gerade nichts.

another dialog with log-file and scoring:

User say: Hello, my name is Peter. Bot say: Nice to meet you, Peter. Guess what my name is? User say: Chalmun? Bot say: Yes. That's right.

log-file:

...
19-08-2018 08:30:48.14078[4][4] - Elapsed time: 0.010 milliseconds
[find_aiml.php][score_matches][269]
Scoring the matches. 
-----------------------
19-08-2018 08:30:48.14120[4][4] - Elapsed time: 0.421 milliseconds
[find_aiml.php][score_matches][562]
Sorted array $allrows:
Array
(
    [0] => Array
        (
            [id] => 4264
            [bot_id] => 1
            [pattern] => <BOT NAME="NAME"/>
            [thatpattern] => * GUESS WHAT MY NAME IS
            [topic] => 
            [filename] => test_2.aiml
            [template] => Yes. That's right.
            [score] => 268
            [current_that] => nice to meet you peter guess what my name is
            [aiml_id] => 4264
            [track_score] => current bot (250 points), thatpattern match with star (2 points), uncommon word match: <bot (8 points), uncom
mon word match: name="name"/> (8 points)
        )

    [1] => Array
        (
            [id] => 4262
            [bot_id] => 1
            [pattern] => <BOT NAME="NAME"/>
            [thatpattern] => 
            [topic] => 
            [filename] => test_2.aiml
            [template] => Yes. This is me. I am <bot name="name"/>.
            [score] => 267
            [current_that] => nice to meet you peter guess what my name is
            [aiml_id] => 4262
            [track_score] => current bot (250 points), no thatpattern to match (1 point), uncommon word match: <bot (8 points), uncommon w
ord match: name="name"/> (8 points)
        )

    [2] => Array
        (
            [id] => 2603
            [bot_id] => 1
            [pattern] => *
            [thatpattern] => 
            [topic] => 
            [filename] => Fragen_keine_Ahnung_V1.aiml
            [template] => <srai>RANDOM PICKUP LINE</srai>
            [score] => 252
            [current_that] => nice to meet you peter guess what my name is
            [aiml_id] => 2603
            [track_score] => current bot (250 points), no thatpattern to match (1 point), pattern star match (1 points)
        )

    [3] => Array
        (
            [id] => 2601
            [bot_id] => 1
            [pattern] => RANDOM PICKUP LINE
            [thatpattern] => 
            [topic] => 
            [filename] => Fragen_keine_Ahnung_V1.aiml
            [template] => <random>
   <li>...Mit der Aussage kann ich im Moment nichts anfangen.</li>
   <li>...Das sagt mir gerade nichts.</li>
   <li>...Mir ist nicht ganz klar, was Sie mir damit sagen möchten...</li>
</random>
            [score] => 251
            [current_that] => nice to meet you peter guess what my name is
            [aiml_id] => 2601
            [track_score] => current bot (250 points), no thatpattern to match (1 point)
        )

)

-----------------------
19-08-2018 08:30:48.14123[2][4] - Elapsed time: 0.027 milliseconds
[find_aiml.php][get_highest_scoring_row][656]
Final candidates:

Array
(
    [0] => Array
        (
            [id] => 4264
            [bot_id] => 1
            [pattern] => <BOT NAME="NAME"/>
            [thatpattern] => * GUESS WHAT MY NAME IS
            [topic] => 
            [filename] => test_2.aiml
            [template] => Yes. That's right.
            [score] => 268
            [current_that] => nice to meet you peter guess what my name is
            [aiml_id] => 4264
            [track_score] => current bot (250 points), thatpattern match with star (2 points), uncommon word match: <bot (8 points), uncommon word match: name="name"/> (8 points)
        )

)

-----------------------
19-08-2018 08:30:48.14149[4][4] - Elapsed time: 0.261 milliseconds
[find_aiml.php][get_highest_scoring_row][711]
Best Response: Array
(
    [id] => 4264
    [bot_id] => 1
    [pattern] => <BOT NAME="NAME"/>
    [thatpattern] => * GUESS WHAT MY NAME IS
    [topic] => 
    [filename] => test_2.aiml
    [template] => Yes. That's right.
    [score] => 268
    [current_that] => nice to meet you peter guess what my name is
    [aiml_id] => 4264
    [track_score] => current bot (250 points), thatpattern match with star (2 points), uncommon word match: <bot (8 points), uncommon word match: name="name"/> (8 points)
)
...

Best regards

Josef-Meier commented 6 years ago

Hello Actually, this should be posted more in a forum as in Github. My general thoughts on this issue: Actually, there should be a reasonable possibility that the bot variables retrieved with <bot name = "xyz" /> can also be processed in a condition tag. How to do that with user variables (predicate variables).

before few definitions from AIML: <bot />: The bot element is used to recall custom bot properties defined in the .properties file. These variables are accessible to all users of the bot.

<set>: The set element is used to set a predicate variable. Predicates are not hardcoded like properties, and can be initialized during conversation. This means that input from the user can be echoed in the value of a predicate

Concrete AIML example for user variables (predicate variables):

...
<category>
    <pattern> HOW ARE YOU FEELING TODAY </pattern>     
    <template>
        <think><set name = "state"> happy</set></think>
        <condition name = "state" value = "happy">I am happy!</condition>
        <condition name = "state" value = "sad">I am sad!</condition>
      </template>
</category>
...

Beispiel für Bot-Variablen (bot properties) : For this I do not have a valid AIML example yet, but the following processing logic:

User say: I guess you're * years old

Now processing of the user input should be realized as follows:

IF bot="gender" == "female" {
   Bot say: "You do not ask a lady about age."
}

IF bot="gender" == "male" OR "undefined" {
   IF <star/> == bot="age" {Bot say: "Good guess. I am bot="age" years old."}
   IF <star/> !=  bot="age" {Bot say: "You are wrong. I am bot="age" years old."}
}

Fictional AIML 3.0 :blush: :

...
<category>
   <pattern>I GUESS YOU ARE  * YEARS OLD</pattern>
   <template>
      <condition <bot="gender"/> EQUAL "female"/>You do not ask a lady about age.</condition>
      <condition <bot="gender"/> EQUAL "male"/>
          <condition <bot="age"/> EQUAL "<star/>"/>Good guess. I am bot="age" years old.</condition>
          <condition <bot="age"/> UNEQUAL "<star/>"/>You are wrong. I am bot="age" years old.</condition>
      </condition>
   </template>
</category>
   ...

My Program-O version 2.6.10 is for AIML 1.0, as far as I understand it. As a user, I feel obliged to at least point to issues and try to substantiate these with concrete examples. But I see not so many chances that features that do not conform to the AIML 1.0 or 1.01 standard will be realized. A new Program-O version for AIML 2.0 has been announced. But that should be more complex as as the current software release. Until then we can enjoy the current version. Everything else is in the hands of the developers. And I'm very confident about that. Best regards

Josef-Meier commented 6 years ago

I'm sorry. I discovered another problem. The test cases before that work, but the following test case with star fails.

<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
  <category>
    <pattern><bot name="name"/> IS *</pattern>
    <template>I should be <star/>?</template>
  </category>
</aiml>

Example dialog: Bot name is "Chalmun". User say: Chalmun is smart. Bot say: I should be? I expect. Bot say: I should be smart?

Get an error like this:

[find_aiml.php][Called outside of function][507]
PHP ERROR [Warning] -preg_match(): Unknown modifier '>' in /srv/www/htdocs/nossl/opensim_lsl/ProgramO/chatbot/core/aiml/find_aiml.php on Line 507

@OverlanAI: Similar to your error message on line 495. Let's see if the development department intends to implement the bot tag in pattern. But I'll be looking for what it could be in my test environment. Best regards

Josef-Meier commented 6 years ago

Because of the test case with "*" which went awry, a little change in the function "unset_all_bad_pattern_matches ()" is necessary. I have therefore updated my first comment. Happy testing :smiley: Best regards

Josef-Meier commented 6 years ago

@OverlanAI Hello OverlanAI, I'm also not such a good php coder. :smiley: Have also tried your change. Your change at your line 499 elseif ($regEx != $category_pattern && (($category_pattern != '*') || ($category_pattern != '_'))) // && preg_match("/$regEx/i", $pattern) != 0) //OLAI change works when it comes to suppressing the error message. This is not a problem at the moment, because it checks for a condition if it is matched then executing any code, because empty brackets follow { }. Here maybe something was kept open by the developer for the future. But still I had the problem, described in my last test case, that the "*" is not replaced. That's the reason why I changed something in the function "unset_all_bad_pattern_matches ()". Here in "pattern" the bot tag <bot name ="XXXX"/> is replaced by the actual value from the "Bot Personality". The developer has already done that with the variable $aiml_pattern. In addition, I have inserted the value in the array $relevantRows under [pattern] with following line. $subrow['pattern'] = $aiml_pattern; So from now on the software uses the actual value from the "Bot Personality" in pattern instead of <bot name ="XXXX"/> . See also my updated first post under "One". Look in source code at the comments with "//// Meier": After the change also works my last published test case with star.

<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
  <category>
    <pattern><bot name="name"/> IS *</pattern>
    <template>I should be <star/>?</template>
  </category>
</aiml>

Example dialog: Bot name is "Bughunter2".

User say: Bughunter2 is smart. Bot say: I should be smart? before Bot say: I should be?

He not process the star. Think I'll finish the topic now and wait for a reaction from the developers. I do not know if the issue is considered.

Best Regards

Program-O commented 6 years ago

Thank you for all the information ... I will look at this over the next few days