CustomIntent mit Dialog

Begonnen von gregorv, 03 Oktober 2024, 10:54:24

Vorheriges Thema - Nächstes Thema

gregorv

Zitat- "return undef":
Jep, war noch nicht getestet - muss return "null" sein. Und klar, nach oben.

Weiterhin unless beseitigt, all eingebaut und  - damit es vollständig ist - noch only damit sind nur die als arg übergebenen Intents enabled.
Neue Varianten (inl. default) getestet - wird aber weiter getestet.

sub _set_sessionIntentFilter {
    my $hash         = shift // return;
    my $data         = shift // return;
    my $intents        = shift;
    my $action       = shift // 'default' ;
    # possible values: default: reset to global intents, enable: enable the intent(s) in $intents, disable: disable the intent(s) in $intents, prefix: just add prefix, all: enable all intents, only: enables only intents from arg
    return "null" if $action eq 'default';
   
    my @allIntents = split m{,}xm, ReadingsVal( $hash->{NAME}, 'intents', '' );
    my @sessionIntents;
    for (@allIntents) {
        next if $_ =~ m{ConfirmAction|CancelAction|Choice|ChoiceRoom|ChoiceDevice};
        push @sessionIntents, $_ if
        !defined $hash->{helper}->{tweaks} ||
        !defined $hash->{helper}{tweaks}->{intentFilter} ||
        !defined $hash->{helper}{tweaks}->{intentFilter}->{$_} ||
        defined $hash->{helper}{tweaks}->{intentFilter}->{$_} && $hash->{helper}{tweaks}->{intentFilter}->{$_} eq 'true';
    }

    if ( $action eq 'all') {
        @sessionIntents = @allIntents;
    }

      my $id = qq($hash->{LANGUAGE}.$hash->{fhemId}:);
    my @tmp;
    my @actionIntents;
    if ( ref $intents eq 'ARRAY' ) {
        @tmp = @$intents;
    } else {
        @tmp = split m{,}xm, $intents;
    }
    for (@tmp) {
        if ( $_ =~ m{\a${id}} ) {
            push @actionIntents, $_;
        } else {
            push @actionIntents, ${id}.$_;
        }
      }
    my @intentFilter;
    @tmp = ();
    @tmp = @{ $data->{intentFilter} } if defined $data->{intentFilter};
    @tmp = @sessionIntents if !defined $data->{intentFilter};
    for my $element (@tmp) {
        if ( $element =~ m{$id}x ) {
            push @intentFilter, $element;
        } else {
            push @intentFilter, ${id}.$element;
        }
      }
    @sessionIntents = ();
    if ( $action eq 'enable' ) {
        for my $element (@actionIntents) {
            push @intentFilter, $element if not grep { $_ eq $element } @intentFilter ;
        }
       @sessionIntents = @intentFilter;      
    }elsif ( $action eq 'disable' ) {
        for my $element (@intentFilter) {
             push @sessionIntents, "$element"  if  ! grep( /^$element$/, @actionIntents ) ;
        }
    }elsif ( $action =~ m{(prefix|all)}x ) {
        for my $element (@intentFilter) {
             push @sessionIntents, "$element" ;
        }
    }elsif ( $action eq 'only') {
        @sessionIntents = @actionIntents;
    }else{
        return "null";
    }
    return \@sessionIntents;
}

gregorv

Anbei meine aktuelle 10_RHASSPY.pm

Basiert auf Deiner vom 29.11. da noch kleine Korrekturen in CommandRef.

Die zwei Keys retryIntent und closeSession geändert auf lower case für den 1. Buchstaben
und einige Änderungen, die ich inzwischen gemacht hatte hauptsächlich neu getOptions() und _set_sessionIntentFilter() und geändert  handleIntentSetNumeric() handleIntentNotRecognized()




Beta-User

Puh, anbei mal das diff.

Ich komme mal zurück auf unser "Arbeitsprogramm":
Zitat von: Beta-User am 17 Oktober 2024, 13:26:12- Das mit "resetInput" fertig machen und einchecken. Dazu müßte man m.E. die angehängten Dateien checken, ob das Zusammenspiel soweit paßt, dass bisherige User keine Probleme haben.
- Danach könnte man sich mit einem globalen (sentences-) Key für "führe den Dialog weiter" befassen. Ziel auch hier: Zwischenversion einchecken.
- Dann eventuell checken, ob man "specials" findet (Zwischenversion...)
Zumindest nach meinen bisherigen (eher wenigen!) Tests sind wir relativ nahe dran, (auch) den zweiten Punkt abhaken zu können. Ich würde weiter gerne erst mal eine "saubere" Zwischenversion haben wollen, bevor wir mit noch mehr Optionen weiter machen.

In dem diff sind einige Dinge drin, die im Sinne dieser "Sauberkeit" sind, aber auch ein paar Sachen, über die ich mir erst mal vertiefter Gedanken machen muss, und bei denen mir vermutlich auch zum Teil der Kontext fehlt (z.B.:
+    respond( $hash, $data, getResponse( $hash, defined $data->{customData}->{silentClosure} ? 'SilentClosure' : 'DefaultConfirmationTimeout' ) ); # @@@ changed to lower caselower case ist klar, aber warum steht an der Stelle der key "eine Ebene tiefer"?

Anders gesagt: Das bekomme ich in der Fülle nicht kurzfristig verarbeitet...

PS: "all" für die IntentFilter-Anfrage brauchen wir (vermutlich) doch nicht. Wenn, dann ist das für die Satz-Testung relevant, und da ist schon "alle Intents" aktiv...
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

gregorv

Zitatlower case ist klar, aber warum steht an der Stelle der key "eine Ebene tiefer"?
habe ich geändert, steht auch in $data

Da war ich etwas irritiert, weil wir hier einen ähnlichen Fall haben. In der Tat geht es da aber gar nicht anders, weil von unseren alten Daten nach einem IntentNotRecognized nur noch die customData intakt sind.
   if ( !$data->{input} || (ref $data->{customData} eq 'HASH' && defined $data->{customData}->{retryIntent} ) ) { # @@@ retry.. lower caseIch neige dazu, dass es besser wäre, wenn wir in der handleIntentNotRecognized() die alten Daten aus customData wieder herstellen (in $reaction kopieren) - dazu müsste dann auch der key intentFilter in customData drin sein. Derzeit ist es so, dass wir den nur aus $hash->{helper}->{".delayed"}->{$data->{sessionId}}->{intentFilter} wieder herstellen können (falls das existiert).
Wenn wir nach einem IntentNotRecognized wichtige Keys nur noch in customData haben, mag es auch an anderen Stellen klemmen.

An der Stelle habe ich auch noch etwas ergänzt. Wenn nämlich reActivateVoiceInput zusammen mit retryInput benutzt wird ist CancelAction nach IntentNotRecognized wieder disabled und wird mit default intentFilter fortgesetzt (da gibt es ja keine oldData). Ich habe den CancelAction da neu aktiviert.
Sieht nun so aus:
    if ( !$data->{input} || (ref $data->{customData} eq 'HASH' && defined $data->{customData}->{retryIntent} ) ) { # @@@ retry.. lower case
        $data->{intentFilter} = $hash->{helper}->{".delayed"}->{$data->{sessionId}}->{intentFilter} if !defined $data->{intentFilter} // "null"; # @@@ NLU IntentNotRecognized does not (allways??)return intentFilter
        $response = $data->{input} ? getResponse( $hash, 'RetryIntent') : 'SilentClosure';
        $reaction = {
            text => $response,
            sendIntentNotRecognized => 'true',
            intentFilter => _set_sessionIntentFilter($hash, $data, 'CancelAction', 'enable' ),   <--- add CancelAction
            customData => $data->{customData}      #Beta-User: toJSON? # @@@ not required



Beta-User

Zitat von: gregorv am 01 Dezember 2024, 14:32:43Wenn wir nach einem IntentNotRecognized wichtige Keys nur noch in customData haben, mag es auch an anderen Stellen klemmen.
Wenn der intent nicht erkannt wird, gibt es imo "nichts" (also keine neuen keys), und mir ist im Moment auch nicht klar, wie der Ablauf für diese "retryIntent"-Sache genau sein soll.
Vermutlich brauchen wir da irgend eine Art von Zwischenschritt, angefangen damit, dass einfach nur "reActivateVoiceInput" aktiv wird, wenn überhaupt ein "input" da ist.

ABER:
Mir ist das im Moment insgesamt zu viel an Änderungen, ich würde gerne erst mal "nur" diese beiden in der commandref bereits (halbwegs) sauber beschriebenen Keys ("reActivateVoiceInput" und "closeSession") einchecken (und den Rest gar nicht erst zeigen). Dazu braucht es eine "saubere" .pm, mal sehen, ob wir das bis Ende der Woche hinbekommen...
Oder gibt es inhaltlich was, das ich übersehen habe und erst mal noch zwingend erforderlich wäre?
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

gregorv

Zitatich würde gerne erst mal "nur" diese beiden in der commandref bereits ...

Die getOptions() wäre schon noch interessant, damit man die Option reActivateVoiceInput auch einstellen kann (statt es in jedem sentence machen zu müssen)

Zitatwie der Ablauf für diese "retryIntent"-Sache genau sein soll
Das ist relativ simpel: Wenn ein IntentNotRecognized kommt soll die Session offen bleiben.
Und da gibt es zwei Fälle die sich nur durch die response unterscheiden - entweder wurde kein input geliefert -> silent weiter machen, ansonsten nach wie bitte? weiter machen.

Beta-User

Zitat von: gregorv am 02 Dezember 2024, 13:33:22Die getOptions() wäre schon noch interessant, damit man die Option reActivateVoiceInput auch einstellen kann (statt es in jedem sentence machen zu müssen)
getOptions() ist mir im Moment noch zu diffus, und wir bekommen da nicht auf die Schnelle die Komplexität hin, die mir nach dem Motto "wenn schon, denn schon!" vorschwebt. Da jetzt aber eine (vereinfachte?) Syntax zu definieren und die dann wieder über den Haufen zu werfen, ist vermutlich für andere User kein wirklich guter Weg. Ergo geht es im Moment imo darum, das (übergangsweise) per sentence einstellen zu können oder global einen "sessionTimeout" zu definieren. Dafür gibt es schon einen key im define. Dann wäre die (künftig imo zwingend weiter bestehende) Option "closeSession" halt die einzige Möglichkeit, vorzeitig das Mikro zu schließen. Für erstes Testen sollte das ok sein und wir bekommen ggf. wenigstens Rückmeldung, wenn es irgendwo unvorhergesehenerweise klemmen sollte.

Zitat von: gregorv am 02 Dezember 2024, 13:33:22
Zitatwie der Ablauf für diese "retryIntent"-Sache genau sein soll
Das ist relativ simpel: Wenn ein IntentNotRecognized kommt soll die Session offen bleiben.
Und da gibt es zwei Fälle die sich nur durch die response unterscheiden - entweder wurde kein input geliefert -> silent weiter machen, ansonsten nach wie bitte? weiter machen.
OK, bin eben auch über diese Code-Stelle gestolpert. Nach kürzerem Nachdenken jetzt ist mir aber immer noch nicht klar, wie der Zusammenhang zwischen $device (specials) oder $intent (tweak) und dieser Option sein soll. Würde es (zumindest für's erste) nicht reichen, auch hier zu schauen, ob wir entweder den globalen key "sessionTimeout" aktiviert haben oder in customData der reActivate-key finden können?

Wie gesagt: Für's erste sollte es eine einfache Möglichkeit geben, die neuen features einigermaßen ungefährdet zu nutzen, feiner einstellbar können wir das später noch machen.

Ansonsten stellt sich ggf. die Frage, ob das "not recognized" schlicht daran lag, dass ein "falscher" intentFilter aktiv war und wie man ggf. damit umgehen kann/soll. Aber das ist imo auch wieder ein Ast, den wir später betrachten sollten...
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

Beta-User

Hmmm, wenn ich das jetzt auf die Schnelle richtig zusammengepuzzelt habe, braucht es doch in der .pm hier dann nur noch eine Abgfrage in handleIntentNotRecognized()?

Also statt
if ( !$data->{input} || (ref $data->{customData} eq 'HASH' && defined $data->{customData}->{RetryIntent} ) ) {
if ( !$data->{input} || (ref $data->{customData} eq 'HASH' && defined $data->{customData}->{RetryIntent} ) || defined $hash->{sessionTimeout} ) {bzw. (erst mal ohne "getOptions")
if ( !$data->{input} || defined $hash->{sessionTimeout} ) {Oder übersehe ich im Moment was?
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

gregorv

Ahh, jetzt verstehe ich Deine Anmerkung im Code, als ich vorgeschlagen hatte die closeSession Bedingung und das Auslesen eines numerischen Wertes daraus als $delay zu streichen. closeSession macht da wirklich keinen Sinn, aber wenn man an sessionTimeout denkt, passt das wieder.

Aber das ist noch etwas komplexer. Wir haben ja zwei Möglichkeiten, einen Dialog offen zu halten. Einmal über respond(), wie es in handleIntentNotRecognized() gemacht wird und außerdem über setDialogTimeout(), wie es in handleCustomIntent gemacht wird. Das hatte ich heute noch einmal probiert,ob man da nicht doch respond() einsetzen kann. - aber wegen vieler Unpässlichkeiten letztlich wieder aufgegeben.
Der große Unterschied zwischen beiden Varianten ist der, dass einmal die Alt-Daten in customData stehen und bei setDialogTimeout() im hash ($hash->{helper}->{".delayed"}->{$data->{sessionId}}->). Das mit dem customData hatten wir ja gemacht, damit die Alt-Daten in eine neue Session übertragen werden können, während das beim schlichten Verlängern einer Session nicht erforderlich ist, weil das hash erhalten bleibt (sessionId bleibt gleich).

Aber ich werde morgen (vmtl. später Vormittag) etwas detaillierter dazu antworten.

gregorv

Zunächst mal zu IntentNotRecognized
Wie ich gestern abend geschrieben habe gibt es da zwei Varianten um auf die Alt-Daten (hier insbesondere intentFilter und retryIntent) zugreifen zu können.
Für die folgenden Szenarien ist reActivateVoiceInput = Dauer und retryInput = 'yes' gesetzt (ist egal was da drin steht 'true' führt aber noch zu JSON Fehler)

Zunächst mal die Szenarien:
  • 1. Wake-Word -> falsches Kommando -> Session wird mit Standard-Ansage beendet:
Grund: bis dahin wurde noch nicht respond() durchlaufen und daher ist in customData kein retryIntent, sondern nur die Wake-Word Id.

  • 2. Wake-Word -> Geräusch -> Session bleibt offen
Grund: derzeit ist die Geräusch-Behandlung unabhängig vom Schalter retryIntent (ist m.E. auch OK so.)

  • 3. Wake-Word -> Kommando -> Response -> sonst noch, was? -> falsches Kommando oder Geräusch -> Session wird fortgesetzt mit wie bitte? oder Stille
Grund: Da sonst noch, was? durchlaufen wurde, gibt es nun Alt-Daten in customData und wegen retryIntent kommt wie bitte?

Die Optionen reActivateVoiceInput = Dauer und retryInput = 'yes' werden - egal, ob sie im sentence oder in tweaks configuriert sind - immer erst gesetzt, wenn das Gesamtsystem weiß, welcher Intent behandelt werden soll. (IntentNotRecognized ist da eine Ausnahme). Insbesondere ist das für Szenario 1. interessant, weil man das weder über sentence noch über getOptions ändern kann

Deine Frage, ob man die tweak/specials nicht besser direkt da auslesen sollte, wo sie benötigt werden hat da übrigens Vorteile, weil hash->tweaks ja unabhängig von irgendwelchen $data Inhalten ist. In Szenario 1. könnte man damit wie bitte? auch über hash->tweaks->retryIntent aktivieren.

Ein Problem sehe ich aber in den ganzen Bedingungen, die bisher nur die getOption berücksichtigen kann - außer man baut so eine Bedingungsprüfung überall, wo man es benötigt.

Ein paar Prints, die aus handleIntentNotRecognized()
Hier $data von Fall 1
2024.12.03 10:32:59 5: [RHASSPY.IF] handleIntentNotRecognized data=
$VAR1 = {
  "confidence" => "0.75",
  "customData" => "Roberta_de_linux_v3_0_0",
  "input" => "mach das keller fenster auf",
  "requestType" => "voice",
  "sessionId" => "arbeitszimmer-Roberta_de_linux_v3_0_0-0bd974b3-a460-4b34-bb25-2d607ce30a18",
  "siteId" => "arbeitszimmer"
};
customData hat keine Alt-Daten weder retryIntent noch intentFilter

Hier $data von Fall 2.
2024.12.03 10:34:06 5: [RHASSPY.IF] handleIntentNotRecognized data=
$VAR1 = {
  "confidence" => "0.75",
  "customData" => "Roberta_de_linux_v3_0_0",
  "input" => "",
  "requestType" => "voice",
  "sessionId" => "arbeitszimmer-Roberta_de_linux_v3_0_0-bb98e1f2-f619-4519-bb88-a785649beae2",
  "siteId" => "arbeitszimmer"
};
also identisch mit Fall 1 ABER unsere Sonderbehandlung wird durchlaufen und respond() wird mit $reaction:
2024.12.03 10:34:06 5: [RHASSPY.IF] handleIntentNotRecognized reaction=
$VAR1 = {
  "customData" => "Roberta_de_linux_v3_0_0",
  "intentFilter" => [
    "de.fhem:Shortcuts",
    "de.fhem:Blinds",
    "de.fhem:ReSpeak",
    "de.fhem:DayNight",
    "de.fhem:GetNumeric",
    "de.fhem:DeviceState",
    "de.fhem:SetOnOff",
    "de.fhem:GetDate",
    "de.fhem:GetTime",
    "de.fhem:CancelAction"
  ],
  "sendIntentNotRecognized" => "true",
  "text" => "SilentClosure"
};
aufgerufen. Da kein intentFilter verfügbar ist, (wie vermutlich immer am Anfang einer Session) werden die default Intents + CancelAction aktiviert.

Anders sieht $data im Fall 3. (falsches Kommando) aus
2024.12.03 11:38:47 5: [RHASSPY.IF] handleIntentNotRecognized data=
$VAR1 = {
  "confidence" => "0.75",
  "customData" => {
    "Device" => "licht",
    "Value" => "on",
    "confidence" => 1,
    "input" => "mach das licht on",
    "intent" => "SetOnOff",
    "lang" => undef,
    "rawInput" => "mach das licht an",
    "reActivateVoiceInput" => 25,
    "requestType" => "voice",
    "retryIntent" => "true",
    "sessionId" => "arbeitszimmer-Roberta_de_linux_v3_0_0-4e1c97ff-f091-4c6c-9a73-cca89e23a398",
    "siteId" => "arbeitszimmer"
  },
  "input" => "mach das keller fenster auf",
  "requestType" => "voice",
  "sessionId" => "c7ecad5c-97c1-4525-880d-6b1e6f9aec29",
  "siteId" => "arbeitszimmer"
};
Hier haben wir (und das gilt auch für alle zukünftigen IntentNotRecognized, die während eine Sitzung noch kommen mögen) in CustomData unsere Alt-Daten.
intentFilter wieder auf default, um CancelAction ergänzt. Da haben wir möglicherweise ein Problem. Das beim Start noch kein intentFilter da ist, ist ja OK, aber sollte durch eine vorherige Intent Behandlung ein intentFilter gesetzt werden, geht der verloren. Hier in diesem Beispiel wird bereits früher CancelAction (in sonst noch was?) gesetzt, kommt in customData aber nicht an. Solange default OK ist wird CancelAction bei IntentNotRecognized ja neu gesetzt, aber wenn der intentFilter verändert wurde, wäre das nicht OK.

Hier noch $reaction für Fall 3
2024.12.03 11:38:47 5: [RHASSPY.IF] handleIntentNotRecognized reaction=
$VAR1 = {
  "customData" => {
    "Device" => "licht",
    "Value" => "on",
    "confidence" => 1,
    "input" => "mach das licht on",
    "intent" => "SetOnOff",
    "lang" => undef,
    "rawInput" => "mach das licht an",
    "reActivateVoiceInput" => 25,
    "requestType" => "voice",
    "retryIntent" => "true",
    "sessionId" => "arbeitszimmer-Roberta_de_linux_v3_0_0-4e1c97ff-f091-4c6c-9a73-cca89e23a398",
    "siteId" => "arbeitszimmer"
  },
  "intentFilter" => [
    "de.fhem:Shortcuts",
    "de.fhem:Blinds",
    "de.fhem:ReSpeak",
    "de.fhem:DayNight",
    "de.fhem:GetNumeric",
    "de.fhem:DeviceState",
    "de.fhem:SetOnOff",
    "de.fhem:GetDate",
    "de.fhem:GetTime",
    "de.fhem:CancelAction"
  ],
  "sendIntentNotRecognized" => "true",
  "text" => "waas: ??"
};

Jetzt kommt ein CustomIntent ( meine Blinds() ) mit einem engen intentFilter. Bei einem CustomIntent wird, wie gestern Abend schon erwähnt die Session mit setDialogTimeout() verlängert.
wieder die drei Fälle von oben (Fall 1. und 2. ist identisch - ist ja noch kein Intent bekannt)
Fall 3 sieht hier etwas anders aus:
  • Wake-Word -> Kommando -> Response -> falsches Kommando oder Stille -> Session wird fortgesetzt entweder mit wie bitte? oder Stille (also kein sonst noch was?)
Die Prints sehen nun anders aus:
$data
$VAR1 = {
  "confidence" => "0.75",
  "customData" => {
    "goAhead" => "yes",
    "name" => "rollladen links",
    "prevPos" => 0,
    "retryIntent" => "yes",
    "room" => "b\x{c3}\x{83}\x{c2}\x{bc}ro"
  },
  "input" => "mach das keller fenster auf",
  "requestType" => "voice",
  "sessionId" => "arbeitszimmer-Roberta_de_linux_v3_0_0-b1809ff1-98da-41da-9b31-b2b1553692f4",
  "siteId" => "arbeitszimmer"
};
in customData stehen nur die Daten, die ich im CustomIntent selbst anhänge intentfilter ist nicht vorhanden, obwohl früher bereits gesetzt. Hier ist der Grund aber ein anderer. In customData kann der nicht drin stehen, weil sonst noch was? gar nicht durchlaufen wurde (und auch nicht soll). Da ich den aber brauche, muss ich die Alt-Daten woanders her holen. Bei einer Session, die mit setDialogTimeout() verlängert wurde stehen die im hash (meist $old_data genannt).
Damit sieht $reaction dann so aus
2024.12.03 12:20:13 5: [RHASSPY.IF] handleIntentNotRecognized reaction=
$VAR1 = {
  "customData" => {
    "goAhead" => "yes",
    "name" => "rollladen links",
    "prevPos" => 0,
    "retryIntent" => "yes",
    "room" => "b\x{c3}\x{83}\x{c2}\x{bc}ro"
  },
  "intentFilter" => [
    "de.fhem:CancelAction",
    "de.fhem:Blinds"
  ],
  "sendIntentNotRecognized" => "true",
  "text" => "wiebitte ??"
};
retryIntent kann abgefragt werden, weil ich das explizit schon in customData übergeben hatte. intentFilter, weil es im hash steht.
Klar könnte man auch retryIntent aus dem hash lesen oder intentFilter könnte als customData schon im CustomIntent angehängt werden Aber einem User ist es sicher nur schwer vermittelbar, dass ein in einem CustomIntent falls er intentFilter benötigt den zwei mal setzen soll (also zusätzlich auf ebene customData)

Übrigens hier noch die hash Einträge für die Optionen tweak und specials
tweaks:
    "tweaks" => {
      "gdt2groups" => {
        "blind" => "rolll\303\244den"
      },
      "reActivateVoiceInput" => {                                             <-----------
        "all" => 35
      },
      "retryIntent" => {                                                      <-----------
        "all" => "true"
      }
    }

und specials
        "AR.Licht" => {
          "alias" => "licht",
          "group_specials" => {
            "async_delay" => "0.5"
          },
          "groups" => "lampen",
          "intents" => {
            "GetOnOff" => {
...          },
          "names" => "licht,lampe,deckenlampe,deckenlicht,b\303\274ro licht",
          "reActivateVoiceInput" => {                                             <-----------
            "all" => 25
          },
          "rooms" => "arbeitszimmer,b\303\274ro"
        },

Beta-User

Kurze Zwischeninfo: Habe mir auch den Code-Teil handleIntentNotRecognized() nochmal angesehen. "Ursprünglich" stand der (aktuell im svn noch deaktivierte) "Stille"-Verarbeitungscode noch am Ende der Funktion, und das war kein (kompletter) Zufall: Wenn ich was deaktiviere bzw. auskommentiere, dann in der Regel an der Stelle, an der ich ihn zuletzt aktiv hatte.

Beim Drübersehen jetzt bin ich dann auch wieder drauf gestoßen, dass wir imo die beiden "Grundtypen" (nach) "continue" (Fall 1) und "reactivate" auch an der Stelle getrennt betrachten müssen. "continue" hat immer Altdaten ($old_data), "reactivate" dann, wenn schon mal eine "Schleife" durch war (Fälle 2a bzw. 2b).

Bei "continue" haben wir dann auch in der Regel einen eingeschränkten Satz an aktivierten Intents, der eigentlich schon dadurch aktiviert bleiben sollte, dass wir innerhalb der session bleiben und keinen neuen setzen. (to be verified)
Fall 2a sollte eigentlich auf "default+cancel" stehen (und auch (immer?) so bleiben?!?), bei 2b stellt sich die Frage, ob wir den verstandenen Inhalt (also nicht "leer") in eine erweiterte Erkennung geben sollten, bei zusätzlich aktivierten Intents (welche?!?).

Imo sollte die Abarbeitung im Code im Großen und Ganzen auch (wieder) dieser Logik folgen, es kann allerdings sein, dass "Stille" der einfachste Fall ist und wir das tatsächlich vorab "abfrühstücken" können (für Stille brauchen wir imo auch keine Fehlerausgabe, das war ein "Abfallprodukt" aus der Test-Suite), mit der aktuellen Logik haben wir also die Ausnahme zur Regel gemacht :o .
2b ist der komplexeste Fall, und im Moment sehe ich auch keine einfache Vorgabe, wie man damit umgehen sollte (eventuell: Erkennung ohne Vorgabe starten und dann eine Bestätigung dieser Aktion anfordern?) Ist aber was für einen Tag...

Und bevor wir irgendwelche "tweaks" oder so einbauen, müssen wir imo diese paar Grundtypen sauber haben, was m.E. am einfachsten geht, wenn wir "retryIntent" einfach global (de-) aktivieren (können). Was spricht dagegen, das (erst mal) über den globalen def-key "sessionTimeout" zu machen?
(Das ist übrigens nicht (direkt) abhängig von "reActivate", das aber über den obigen key halt im Moment zusätzlich auch als "global aktiv" gilt).
(Ich gehe davon aus, dass jede zusätzliche Option schlicht die Komplexität verdoppelt, deswegen will ich mich erst mal nicht mit mehr Optionen belasten als unbedingt nötig...)
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

gregorv

#221
der eigentlich schon dadurch aktiviert bleiben sollte, dass wir innerhalb der session bleibenhabe ich gerade geprüft, stimmt. Bei continous bleibt der intentFilter erhalten, wenn der key intentFilter in $reaction nicht mitgegeben wird.
nein, funktioniert doch nicht. Ich hatte offenbar kein reload gemacht, als ich das getestet habe. Wenn  intentFilter in $reaction nicht mitgegeben wird, gilt der default.

Was spricht dagegen, das (erst mal) über den globalen def-key "sessionTimeout" zu machen?werde ich mal testen. Bisher war ich nicht sicher, ob man sich darauf verlassen kann, ob der nicht im Verlauf einer Session entstehen oder verschwinden kann. Speziell für einen CustomIntent ist das ja kein muss-key - wenn sessionTimeout nicht übergeben wird, gilt ja default. Ich werde das mal überdenken.
Bisher habe ich sessionTimeout nur als Variable betrachtet, um die Dauer eines Dialoges setzen zu können, als key weiß ich zwar, dass der existieren kann ($hash->{helper}->{msgDialog}->{config}->{sessionTimeout}), hatte damit bisher aber nichts zu tun und habe ich auch noch nie im hash gesehen. Für mich sah das eher so aus, als gehört das zum Bereich text-dialog. Darf man den so einfach erstellen? - es fehlt auch die gesamte Kette davor (->{msgDialog}->{config}). Falls ja, wäre es sicher auch sinnvoll die anderen Parameter da hinein zu schreiben - hat ja alles irgendwie mit config für einen Dialog zu tun.

bei 2b stellt sich die Frage, ob wir den verstandenen Inhalt (also nicht "leer") Diesen Codeteil habe ich zwar schon gesehen, aber nicht so angeschaut, dass ich das beurteilen könnte, wie man das hier nutzen kann. Mir scheint aber, dass diese Funktion ohnehin schon aktiv ist. Wenn ich nicht ein völlig blödsinniges Kommando gebe, sondern z.B. mach das Bürolicht im Flur an dann kommt kein wie bitte? sondern ein Hinweis über widersprüchliche Angaben.
2024.12.04 10:40:12 5: Parsed value: flur for key: Room
2024.12.04 10:40:12 5: Parsed value: SetOnOff for key: intent
2024.12.04 10:40:12 5: Parsed value: mach das büro licht in dem flur an for key: rawInput
2024.12.04 10:40:12 5: Parsed value: on for key: Value
2024.12.04 10:40:12 5: Parsed value: büro licht for key: Device
...
2024.12.04 10:40:12 5: Device selected (by hash, using only name): AR.Licht
2024.12.04 10:40:12 5: [RHASSPY.IF] getOptions called
2024.12.04 10:40:12 5: handleIntentSetOnOff called
2024.12.04 10:40:12 5: [RHASSPY.IF] getNeedsClarification called, regex is büro licht
2024.12.04 10:40:12 5: [RHASSPY.IF] resetting Timer: RHASSPY.IF_63ce7db8-8753-4091-8d30-54b817592b52
Es kommt in diesem Fall nicht zu einem IntentNotRecognized
Aber dazu habe ich ohnehin noch eine Frage - wozu gibt es den Key .ENABLED ?
  "customData" => {
    ".ENABLED" => [
      "Choice",
      "CancelAction"
    ],
ich hätte statt .ENABLED eher intentFilter erwartet.

Fall 2a sollte eigentlich auf "default+cancel" stehen (und auch (immer?) so bleiben?!?)Das wäre OK. Es gibt da FÜR und WIDER. Bei versehentlicher Aktivierung ausgelöst durch z.B. Fernseher, Radio, ... ist es sicher besser wenn die Session beendet wird, bei Betrachtung einer sonst noch etwas?-Session ist es nicht ganz konsistent wenn die wie bitte? Rückfrage nicht schon am Anfang funktioniert.


Beta-User

Vorab mal zu "sessionTimeout": Das ist vorrangig mal ein globaler key, der im RHASSPY-define angegeben werden kann - der fehlt nur "oben" in der Liste, habe ich grade festgestellt... "Unten" (Parameters) ist er als "experimental" gelistet.
RHASSPY liest den im Moment nur aus, ändert da aber im laufenden Betrieb nichts.

Es gibt dann für "textbasierte" Dialoge noch die Option, den separat zu setzen, aber ausgewertet wird dieser Teil dann nur, wenn auf der anderen Seite auch ein Messenger oä. vorhanden ist; dieser Wert geht dann (und nur dann) vor.

(Zu guter letzt kann man dann auch noch per Reading (pro siteId) dynamisch reagieren, wenn man will...)

Zitat von: gregorv am 04 Dezember 2024, 11:58:28Diesen Codeteil habe ich zwar schon gesehen, aber nicht so angeschaut, dass ich das beurteilen könnte, wie man das hier nutzen kann. Mir scheint aber, dass diese Funktion ohnehin schon aktiv ist. Wenn ich nicht ein völlig blödsinniges Kommando gebe, sondern z.B. mach das Bürolicht im Flur an dann kommt kein wie bitte? sondern ein Hinweis über widersprüchliche Angaben.
Muss vielleicht ausholen:
Wenn ein Intent erkannt wird, wird auch versucht, den auszuführen, und wenn das nicht klappt, gibt es eben eine Rückfrage (über die Qualität bzw. Verbesserungsmöglichkeiten können wir uns ggf. auch noch austauschen).
"Not recognized" kommt ggf. aber dann zustande, wenn der gesprochene (und von der STT-Komponente korrekt "decodierte") Satz zwar bekannt ist, aber grade der zugehörige Intent nicht aktiviert ist.
Letztlich bin ich bei meinen "damaligen" Versuchen dann irgendwo in dieser Ecke etwas ratlos gestrandet, und da es auch niemanden gab, der an einer (wie auch immer gearteten) Lösung dieser Konstellation Interesse gezeigt hatte, habe ich dann eben den Teil auskommentiert, soweit er nicht für die "Basisunktionalität" erforderlich war.
Aus diesen Versuchen stammt übrigens auch der "experimental-key" "sessionTimeout" (für voice-Dialoge)...


Zitat von: gregorv am 04 Dezember 2024, 11:58:28wozu gibt es den Key .ENABLED ?
Gute Frage. Müßte auch im Code schauen, ob der überhaupt irgendwo benutzt wird, und wenn ja, ob man das nicht anderweitig "besser" lösen könnte (es kommt ja der aktive intentFilter von Rhasspy zurück, allerdings "individualisiert").
Ist vermutlich (!) auch noch ein Rest von dem damaligen "Versuchs-Code" und könnte eventuell entfallen, wenn wir uns an die Bereinigung machen.

Diese ganzen "Unsauberkeiten" (?) sind übrigens mit der Grund, warum ich gerne zuerst einen funktionalen Basis-Code hätte, bevor wir anfangen, das wieder komplizierter zu machen ;D ...

Zitat von: gregorv am 04 Dezember 2024, 11:58:28Das wäre OK. Es gibt da FÜR und WIDER. Bei versehentlicher Aktivierung ausgelöst durch z.B. Fernseher, Radio, ... ist es sicher besser wenn die Session beendet wird, bei Betrachtung einer sonst noch etwas?-Session ist es nicht ganz konsistent wenn die wie bitte? Rückfrage nicht schon am Anfang funktioniert.
? Ich glaube, das habe ich noch nicht durchschaut, wie es gemeint ist.

Habe jetzt nochmal die "not recognized"-Funktion durchgesehen, und würde mal folgendes in den Raum werfen (wie üblich: ungetestet...):

sub handleIntentNotRecognized {
    my $hash = shift // return;
    my $data = shift // return;

    Log3( $hash, 5, "[$hash->{NAME}] handleIntentNotRecognized called, input is $data->{input}" );

    my $reaction;
   
    if ( !$data->{input} ) { # just listened to "noise" or silence
        $data->{requestType} //= 'voice'; # we need "voice" to make sure we reopen the microphone if session is voice type
        $reaction = {
            text => 'SilentClosure',
            sendIntentNotRecognized => 'true',
            customData => $data->{customData}
        };
        return respond( $hash, $data, $reaction);  # continue session silently
    }

    my $identity = qq($data->{sessionId});
    my $siteId = $hash->{siteId};
    my $msgdev = (split m{_${siteId}_}x, $identity,3)[0];

    if ($msgdev && $msgdev ne $identity) {
        $data->{text} = getResponse( $hash, 'NoIntentRecognized' );
        return handleTtsMsgDialog($hash,$data);
    }

    #return $hash->{NAME} if !$hash->{experimental};

    my $data_old = $hash->{helper}{'.delayed'}->{$identity};

    if ( !defined $data_old ) {
       
return handleCustomIntent($hash, 'intentNotRecognized', $data) if defined $hash->{helper}{custom} && defined $hash->{helper}{custom}{intentNotRecognized};
        my $entry = qq([$data->{siteId}] $data->{input});
        readingsSingleUpdate($hash, 'intentNotRecognized', $entry, 1);
        #Beta-User: silence chuncks or single words, might later be configurable
        #if ( !defined $data->{input} || length($data->{input}) < 12 ) {
        #if ( !$data->{input} || (ref $data->{customData} eq 'HASH' && defined $data->{customData}->{RetryIntent} ) ) {
        if ( defined $hash->{sessionTimeout} ) { #this might be the place to extend for "getOption" keys
            $data->{requestType} //= 'voice'; # we need "voice" to make sure we reopen the microphone if session is voice type
            $reaction = {
                text => getResponse( $hash, 'RetryIntent'),
                sendIntentNotRecognized => 'true',
                customData => $data->{customData}
            };
            return respond( $hash, $data, $reaction);  # continue listening
        }

        $data->{requestType} //= 'text';
        return respond( $hash, $data, getResponse( $hash, 'NoIntentRecognized' ));
    }

    #$data_old is given, continue dialogue
    $data->{requestType} //= $data_old->{requestType} // 'voice'; # required, otherwise session open but no voice input possible
    $reaction = {
        text => getResponse( $hash, 'RetryIntent'),
        sendIntentNotRecognized => 'true',
        customData => $data->{customData}
    };
    respond( $hash, $data, $reaction); # keep session open and continue listening
    return $hash->{NAME};
}
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

gregorv

#223
Ich habe die handleIntentNotRecognized() mal mit Deiner Version getauscht. Zwei Dinge fallen auf:
Bei einem CustomIntent kommt nach IntentNotRecognized die Nachfrage wie bitte?. Ab da ist der intentfilter auf default.
Bei einem normalen Intent kommt nach IntentNotRecognized nur die Default Ansage und die Session ist beendet (das wie bitte? da, ist erst als Zukunftsfeature geplant, wenn ich Dich richtig verstanden habe?).
(in beiden Fällen reActivateVoiceInput und retryIntent aktiv und IntentNotRecognized mit input)
Bei einem CustomIntent oder normalen Intent wird IntentNotRecognized ohne input still übergangen und die Session bleibt offen. Der intentFilter ist anschließend default (u.A. CancelAction weg)

Ich teste aber noch weiter und verfolge erst mal den Weg, der im Code durchlaufen wird.

Zitat? Ich glaube, das habe ich noch nicht durchschaut, wie es gemeint ist.
Das galt für den Fall, dass reActivateVoiceInput und retryIntent aktiv sind.
retryIntent bewirkt ja (zumindest in meiner Variante), dass bei einem IntentNotRecognized mit input != leer nicht die Default Ansage kommt, sondern die Nachfrage wie bitte?.
Wenn die Aktivierung (erkanntes Wake-Word) unbeabsichtigt ist, weil das Wake-Word zufällig z.B. aus dem Radio erkannt wurde, ist es besser, wenn ein anschließendes IntentNotRecognized zum Ende der Session führt. Das ist aber nicht konsistent mit einer sonst noch was? Session, weil da erst NACH dem ersten sonst noch was?  ein IntentNotRecognized das wie bitte? auslöst. Somit gibt es beim Start der Session (also nach Wake-Word) ein anderes Verhalten auf intentNotRecognized, als nach einem sonst noch was?. Ich neige aber dazu, dieses unterschiedliche Verhalten als Feature zu verkaufen, weil man sonst eventuell länger dauernde Unterhaltungen zwischen Radio und Rhasspy provoziert.

gregorv

ZitatVorab mal zu "sessionTimeout": Das ist vorrangig mal ein globaler key ...
Wenn ich das richtig verstehe, wäre das nicht ganz störungssicher, weil es sein könnte, dass jemand den Key gesetzt hat.
Du hattest ja vorgeschlagen mit sessionTimeout den Key retryIntent abzulösen - oder irre ich mich da?
Wenn das so wäre ist bei RHASSPY Instanzen wo der Key sessionTimeout im define steht nach einem Update plötzlich wie bitte? aktiv.

Willst Du reActivateVoiceInput auch da unterbringen ?

Oder bringe ich da jetzt was ganz Anderes durcheinander?  :(