@@ -147,9 +147,9 @@ func (h *reactiveHandler) actionQuery(ctx context.Context, dec router.Decision)
// The previous question cannot be re-asked for another day. Saying so
// beats "не знаю", which reads as "no data for tomorrow" when the
// truth is that she never looked.
return "про другой день так не отвечу — спроси целиком."
return phraser . Q ( phraser . QueryOtherDay , nil )
}
return "не знаю."
return phraser . Q ( phraser . QueryUnknown , nil )
}
// queryFactByKey — when the dialogue layer resolved an anaphoric reference to
@@ -167,11 +167,11 @@ func (h *reactiveHandler) queryFactByKey(ctx context.Context, t *queryTurn) (str
if dec . Slots . HasTime {
// The query asks about timing — the fact's own timestamp is the
// answer it's looking for. Format as a natural reply.
return fmt . Sprintf ( "я записала это %s ", formatTime ( f . Ts ) ) , true
return phraser . Q ( phraser . QueryFactWhen , map [ string ] string { "when ": formatTime ( f . Ts ) } ) , true
}
// General fact reference: describe what we know.
if dec . Utterance == "" {
return fmt . Sprintf ( "вот что я знаю: %s — %s ", dec . Slots . Key , f . Value ) , true
return phraser . Q ( phraser . QueryFactValue , map [ string ] string { "key ": dec . Slots . Key , "value" : f . Value } ) , true
}
// The utterance still carries the question; fall through to normal RAG
// with the resolved key in context.
@@ -197,7 +197,7 @@ func (h *reactiveHandler) queryDayPlan(ctx context.Context, t *queryTurn) (strin
plan , err := h . api . DayPlan ( ctx )
if err != nil {
log . Printf ( "voice: day plan: %v" , err )
return "не получилось собрать план." , true
return phraser . Q ( phraser . QueryFailPlan , nil ) , true
}
if ! router . IsRestOfDayQuery ( t . dec . Utterance ) {
return plan . Spoken , true
@@ -242,7 +242,7 @@ func (h *reactiveHandler) queryHabits(ctx context.Context, t *queryTurn) (string
facts , err := h . api . RecentActiveFactsByKind ( ctx , string ( store . KindSelf ) , habitFactWindow )
if err != nil {
log . Printf ( "voice: habits: recent facts: %v" , err )
return "не получилось посмотреть записи." , true
return phraser . Q ( phraser . QueryFailNotes , nil ) , true
}
obs := make ( [ ] memory . Observation , 0 , len ( facts ) )
for _ , f := range facts {
@@ -281,7 +281,7 @@ func (h *reactiveHandler) queryFeeds(ctx context.Context, t *queryTurn) (string,
// Claim the turn rather than fall through: "не читаю ленты" is true, and
// letting general knowledge answer "что нового?" would be an invented
// news bulletin.
return "я пока не читаю ленты — они не настроены." , true
return phraser . Q ( phraser . QueryFeedsOff , nil ) , true
}
// By source, not the last 200 notes of any kind: a busy day of voice notes
// used to push the newest headline out of the window, and she answered "в
@@ -289,7 +289,7 @@ func (h *reactiveHandler) queryFeeds(ctx context.Context, t *queryTurn) (string,
notes , err := h . api . RecentNotesFromSource ( ctx , rss . SourcePrefix , feedNoteWindow )
if err != nil {
log . Printf ( "voice: feeds: recent notes: %v" , err )
return "не получилось посмотреть ленты." , true
return phraser . Q ( phraser . QueryFailFeeds , nil ) , true
}
var picked [ ] string
for _ , n := range notes {
@@ -306,11 +306,11 @@ func (h *reactiveHandler) queryFeeds(ctx context.Context, t *queryTurn) (string,
}
if len ( picked ) == 0 {
if q . Category != "" {
return "по этой теме в лентах пока ничего." , true
return phraser . Q ( phraser . QueryFeedsTopic , nil ) , true
}
return "в лентах пока ничего нового." , true
return phraser . Q ( phraser . QueryFeedsEmpty , nil ) , true
}
return "вот что нового: " + strings . Join ( picked , "; " ) , true
return phraser . Q ( phraser . QueryFeedsNew , map [ string ] string { "items" : strings . Join ( picked , "; " ) } ) , true
}
// queryCalendar — "что у меня сегодня?", "планы на завтра?"
@@ -324,7 +324,7 @@ func (h *reactiveHandler) queryCalendar(ctx context.Context, t *queryTurn) (stri
events , err := h . api . CalendarEvents ( ctx , date , date . Add ( 24 * time . Hour ) )
if err != nil {
log . Printf ( "voice: calendar events: %v" , err )
return "не получилось проверить календарь." , true
return phraser . Q ( phraser . QueryFailCalendar , nil ) , true
}
// Provenance travels with each event. A work meeting relayed off a phone
// notification (source ambient:notif, #126) is stored below full confidence
@@ -382,19 +382,23 @@ func (h *reactiveHandler) queryWeather(ctx context.Context, t *queryTurn) (strin
if loc == "" {
// He named no city and voice.weather.default_location is unset. Saying
// so is the only honest answer; picking a city would be inventing one.
return "не знаю, для какого города — задай voice.weather.default_location или назови город." , true
return phraser . Q ( phraser . QueryWeatherWhere , nil ) , true
}
ctxWT , cancel := context . WithTimeout ( ctx , 5 * time . Second )
defer cancel ( )
w , err := h . weatherProvider . CurrentWeather ( ctxWT , loc )
if errors . Is ( err , weather . ErrNotConfigured ) {
return "погода не настроена." , true
return phraser . Q ( phraser . QueryWeatherOff , nil ) , true
}
if err != nil {
log . Printf ( "voice: weather: %v" , err )
return "не получилось узнать погоду." , true
return phraser . Q ( phraser . QueryFailWeather , nil ) , true
}
return fmt . Sprintf ( "в %s сейчас %.0f градусов, %s." , w . Location , w . Temperature , w . Condition ) , true
return phraser . Q ( phraser . QueryWeatherNow , map [ string ] string {
"location" : w . Location ,
"temp" : fmt . Sprintf ( "%.0f" , w . Temperature ) ,
"condition" : w . Condition ,
} ) , true
}
// queryEmbed isn't an answer source — it's the shared cost the two recall
@@ -404,7 +408,7 @@ func (h *reactiveHandler) queryEmbed(ctx context.Context, t *queryTurn) (string,
vec , err := router . EmbedQuery ( ctx , h . embedder , t . dec . Utterance )
if err != nil {
log . Printf ( "voice: embed query: %v" , err )
return "не получилось найти ответ." , true
return phraser . Q ( phraser . QueryFailAnswer , nil ) , true
}
t . vec = vec
return "" , false
@@ -473,7 +477,7 @@ func (h *reactiveHandler) queryNotes(ctx context.Context, t *queryTurn) (string,
notes , err := h . api . QueryNotes ( ctx , t . vec , 5 )
if err != nil {
log . Printf ( "voice: query notes: %v" , err )
return "не получилось найти ответ." , true
return phraser . Q ( phraser . QueryFailAnswer , nil ) , true
}
t . notes = notes
noteScores := make ( [ ] float64 , len ( notes ) )
@@ -498,7 +502,7 @@ func (h *reactiveHandler) queryNotes(ctx context.Context, t *queryTurn) (string,
log . Printf ( "voice: phrase query: %v" , err )
}
if reply == "" {
reply = "вот что я нашла: " + texts [ 0 ]
reply = phraser . Q ( phraser . QueryFound , map [ string ] string { "text" : texts [ 0 ] } )
}
return reply , true
}
@@ -532,13 +536,13 @@ func (h *reactiveHandler) queryWeb(ctx context.Context, t *queryTurn) (string, b
page , err := h . crawler . Page ( ctxFetch , link )
if err != nil {
if errors . Is ( err , crawl . ErrRobots ) {
return "эта страница закрыта для чтения — robots.txt не разрешает." , true
return phraser . Q ( phraser . QueryPageBlocked , nil ) , true
}
log . Printf ( "voice: web: %v" , err )
return "не получилось прочитать страницу." , true
return phraser . Q ( phraser . QueryFailPage , nil ) , true
}
if page . Text == "" {
return "страница открылась, но читать там нечего." , true
return phraser . Q ( phraser . QueryPageEmpty , nil ) , true
}
// The page is handed to the phraser the same way a note is: as context for
// the question he actually asked. She answers the question, she does not
@@ -548,7 +552,7 @@ func (h *reactiveHandler) queryWeb(ctx context.Context, t *queryTurn) (string, b
if reply == "" {
// No phraser (or it failed): read back the top of the page rather than
// pretend the fetch did not happen.
return "вот что на странице: " + crawl . TrimRunes ( page . Text , 300 ) , true
return phraser . Q ( phraser . QueryPageText , map [ string ] string { "text" : crawl . TrimRunes ( page . Text , 300 ) } ) , true
}
return reply , true
}
@@ -614,7 +618,7 @@ func (h *reactiveHandler) querySearch(ctx context.Context, t *queryTurn) (string
if reply == "" {
// No phraser, or it failed. Read back the best evidence rather than
// pretend the search did not happen.
return "вот что я нашла: " + crawl . TrimRunes ( resp . Snippets ( ) [ 0 ] , 300 ) , true
return phraser . Q ( phraser . QueryFound , map [ string ] string { "text" : crawl . TrimRunes ( resp . Snippets ( ) [ 0 ] , 300 ) } ) , true
}
return reply , true
}
@@ -695,7 +699,7 @@ func (h *reactiveHandler) queryKiwix(ctx context.Context, t *queryTurn) (string,
if reply == "" {
// No phraser, or it failed. Read back the best hit rather than pretend
// the search did not happen.
return "вот что я нашла: " + crawl . TrimRunes ( top . Title + " — " + page . Text , 300 ) , true
return phraser . Q ( phraser . QueryFound , map [ string ] string { "text" : crawl . TrimRunes ( top . Title + " — " + page . Text , 300 ) } ) , true
}
return reply , true
}
@@ -725,7 +729,7 @@ func (h *reactiveHandler) queryPersonal(ctx context.Context, t *queryTurn) (stri
return "" , false
}
log . Printf ( "voice: %q is about him and his own data did not answer it; not asking the world" , t . dec . Utterance )
return "не знаю — не нашла у тебя такой записи." , true
return phraser . Q ( phraser . QueryPersonalNone , nil ) , true
}
// personalMarkers — first-person POSSESSION, not first person generally.
@@ -793,7 +797,7 @@ func (h *reactiveHandler) queryGeneral(ctx context.Context, t *queryTurn) (strin
if h . phraser == nil {
// No model of any size. That is not the workstation being asleep, so it
// is not that gap: it is simply not knowing.
return "не знаю." , true
return phraser . Q ( phraser . QueryUnknown , nil ) , true
}
reply , err := h . phraseWorld ( ctx , t . dec . Utterance , nil )
if errors . Is ( err , phraser . ErrNoWorldModel ) {
@@ -801,7 +805,7 @@ func (h *reactiveHandler) queryGeneral(ctx context.Context, t *queryTurn) (strin
return worldGap ( ) , true
}
if err != nil || reply == "" {
return "не знаю." , true
return phraser . Q ( phraser . QueryUnknown , nil ) , true
}
return reply , true
}