Merge the list read-back seeds (#179)
This commit is contained in:
@@ -95,6 +95,12 @@ func (h *reactiveHandler) removeListItem(ctx context.Context, cap router.ListCap
|
|||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// listFloor — the keyword test behind topicList, in the shape turnIsAbout takes.
|
||||||
|
func listFloor(u string) bool {
|
||||||
|
_, ok := router.ParseListQuery(u)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
// queryList — "что в списке покупок?", "что мне купить?".
|
// queryList — "что в списке покупок?", "что мне купить?".
|
||||||
//
|
//
|
||||||
// A query source, so it sits in querySources and either claims the turn or
|
// A query source, so it sits in querySources and either claims the turn or
|
||||||
@@ -102,10 +108,15 @@ func (h *reactiveHandler) removeListItem(ctx context.Context, cap router.ListCap
|
|||||||
// source is: the notes pass would otherwise answer a list question with
|
// source is: the notes pass would otherwise answer a list question with
|
||||||
// whatever note is nearest.
|
// whatever note is nearest.
|
||||||
func (h *reactiveHandler) queryList(ctx context.Context, t *queryTurn) (string, bool) {
|
func (h *reactiveHandler) queryList(ctx context.Context, t *queryTurn) (string, bool) {
|
||||||
list, ok := router.ParseListQuery(t.dec.Utterance)
|
if h.dataStore == nil {
|
||||||
if !ok || h.dataStore == nil {
|
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
// The seeds decide the subject and listQueryPrefixes is the floor behind
|
||||||
|
// them (V-522). Which list he named is a noun lookup either way.
|
||||||
|
if !h.turnIsAbout(ctx, t, topicList, listFloor) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
list := router.ListNamedIn(t.dec.Utterance)
|
||||||
items, err := h.dataStore.ListItems(ctx, list, "")
|
items, err := h.dataStore.ListItems(ctx, list, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("voice: list items: %v", err)
|
log.Printf("voice: list items: %v", err)
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ const (
|
|||||||
topicNetwork topicLabel = "network"
|
topicNetwork topicLabel = "network"
|
||||||
topicAttend topicLabel = "attention"
|
topicAttend topicLabel = "attention"
|
||||||
topicFeed topicLabel = "feeds"
|
topicFeed topicLabel = "feeds"
|
||||||
|
topicList topicLabel = "list"
|
||||||
topicOther topicLabel = "other"
|
topicOther topicLabel = "other"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -140,7 +141,25 @@ var topicSeedSets = map[topicLabel][]string{
|
|||||||
"what is new in the feeds",
|
"what is new in the feeds",
|
||||||
"any news headlines today",
|
"any news headlines today",
|
||||||
},
|
},
|
||||||
|
// Reading a standing list back, and only that. Adding to one and clearing
|
||||||
|
// one stay on the phrase tables in internal/router/list.go — see its header
|
||||||
|
// for why a span and a delete are not seed-shaped work.
|
||||||
|
topicList: {
|
||||||
|
"что в списке покупок",
|
||||||
|
"что мне нужно купить",
|
||||||
|
"прочитай список покупок",
|
||||||
|
"покажи что в списке",
|
||||||
|
"что осталось купить в магазине",
|
||||||
|
"что мне нужно в аптеке",
|
||||||
|
"какой у меня список покупок",
|
||||||
|
"what is on my shopping list",
|
||||||
|
"read me the grocery list",
|
||||||
|
},
|
||||||
topicOther: {
|
topicOther: {
|
||||||
|
// A task question is not a list read-back. They collide on "что у меня",
|
||||||
|
// and the list has its own table to lose to as well.
|
||||||
|
"какие у меня задачи",
|
||||||
|
"что у меня в делах",
|
||||||
// The bare newness opener, which is a greeting and not a request for
|
// The bare newness opener, which is a greeting and not a request for
|
||||||
// headlines. It sits here on purpose: it is close enough to the feed
|
// headlines. It sits here on purpose: it is close enough to the feed
|
||||||
// seeds that it will not clear topicMargin, and a thin call goes to
|
// seeds that it will not clear topicMargin, and a thin call goes to
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ func TestTopicFloorAnswersWithoutSeeds(t *testing.T) {
|
|||||||
{"какие устройства в сети?", topicNetwork, isNetworkQuery, true},
|
{"какие устройства в сети?", topicNetwork, isNetworkQuery, true},
|
||||||
{"что требует внимания?", topicAttend, isAttentionQuery, true},
|
{"что требует внимания?", topicAttend, isAttentionQuery, true},
|
||||||
{"что нового в лентах?", topicFeed, feedFloor, true},
|
{"что нового в лентах?", topicFeed, feedFloor, true},
|
||||||
|
{"что в списке покупок?", topicList, listFloor, true},
|
||||||
{"почему небо синее", topicWeather, isWeatherQuery, false},
|
{"почему небо синее", topicWeather, isWeatherQuery, false},
|
||||||
{"я дома", topicHome, isHomeQuery, false},
|
{"я дома", topicHome, isHomeQuery, false},
|
||||||
{"интернет не работает", topicNetwork, isNetworkQuery, false},
|
{"интернет не работает", topicNetwork, isNetworkQuery, false},
|
||||||
@@ -93,6 +94,12 @@ func TestONNXTopics(t *testing.T) {
|
|||||||
// The greeting. It has to lose to topicOther, or fall thin enough that
|
// The greeting. It has to lose to topicOther, or fall thin enough that
|
||||||
// ParseFeedQuery — which declines a vague noun with no topic — answers.
|
// ParseFeedQuery — which declines a vague noun with no topic — answers.
|
||||||
{"что нового?", topicOther, feedFloor},
|
{"что нового?", topicOther, feedFloor},
|
||||||
|
{"что мне надо купить в магазине", topicList, listFloor},
|
||||||
|
{"прочитай мне список", topicList, listFloor},
|
||||||
|
{"что там в аптеке нужно взять", topicList, listFloor},
|
||||||
|
// A task read-back is not a list read-back, and the two collide on
|
||||||
|
// "что у меня".
|
||||||
|
{"какие у меня сейчас задачи", topicOther, listFloor},
|
||||||
}
|
}
|
||||||
|
|
||||||
h := &reactiveHandler{recall: recallWiring{embedder: emb}}
|
h := &reactiveHandler{recall: recallWiring{embedder: emb}}
|
||||||
|
|||||||
@@ -17,6 +17,15 @@ import (
|
|||||||
// The markers are deliberately explicit. "молоко закончилось" is an
|
// The markers are deliberately explicit. "молоко закончилось" is an
|
||||||
// observation about the world and belongs in a note; only an instruction to
|
// observation about the world and belongs in a note; only an instruction to
|
||||||
// put something on a list puts it there.
|
// put something on a list puts it there.
|
||||||
|
//
|
||||||
|
// The four paths split on 05-08-2026, and the split is by what the caller needs
|
||||||
|
// rather than by language (V-522). Reading a list back needs one bit — is this
|
||||||
|
// about the list — so the seeds decide it, topicList through turnIsAbout, and
|
||||||
|
// listQueryPrefixes below is the offline floor. The other three keep the tables
|
||||||
|
// as the answer. Add and remove need to know WHERE the item starts, and a
|
||||||
|
// cosine over a whole utterance does not say which byte the milk begins at.
|
||||||
|
// Clear DELETES the list, so it stays on exact phrases: a false claim there
|
||||||
|
// loses rows he cannot get back, which is not the trade a margin makes.
|
||||||
|
|
||||||
// listTags — the lists he can name, as one dictionary form each. Russian
|
// listTags — the lists he can name, as one dictionary form each. Russian
|
||||||
// declines the tag ("список покупок", "в покупки", "в покупках"), and the
|
// declines the tag ("список покупок", "в покупки", "в покупках"), and the
|
||||||
@@ -186,6 +195,31 @@ func ParseListQuery(text string) (string, bool) {
|
|||||||
return list, true
|
return list, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListNamedIn reports which standing list an utterance names, anywhere in it,
|
||||||
|
// defaulting to покупки when it names none.
|
||||||
|
//
|
||||||
|
// takeListTag is not enough for a read-back, because it reads the FRONT of a
|
||||||
|
// remainder a prefix table has already eaten. The seeds claim a read-back
|
||||||
|
// without eating anything (topicList, cmd/mavend/topics.go, V-522), so "что мне
|
||||||
|
// нужно в аптеке" has to be scanned rather than trimmed. A list name is a noun
|
||||||
|
// in the dictionary, so this is a lookup and decides nothing about meaning.
|
||||||
|
func ListNamedIn(text string) string {
|
||||||
|
for _, f := range strings.Fields(strings.ToLower(text)) {
|
||||||
|
head := strings.Trim(f, listTrimCut)
|
||||||
|
for _, s := range listTags {
|
||||||
|
if morph.SameWord(head, s.word) {
|
||||||
|
return s.list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, s := range listTagsEN {
|
||||||
|
if head == s.word {
|
||||||
|
return s.list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "покупки"
|
||||||
|
}
|
||||||
|
|
||||||
// ParseListClear reports whether an utterance crosses off a whole list.
|
// ParseListClear reports whether an utterance crosses off a whole list.
|
||||||
func ParseListClear(text string) (string, bool) {
|
func ParseListClear(text string) (string, bool) {
|
||||||
lower := strings.ToLower(strings.Trim(strings.TrimSpace(text), listTrimCut))
|
lower := strings.ToLower(strings.Trim(strings.TrimSpace(text), listTrimCut))
|
||||||
|
|||||||
Reference in New Issue
Block a user