145 lines
6.8 KiB
Go
145 lines
6.8 KiB
Go
package domain
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
"unicode/utf8"
|
|
|
|
"git.cin.su/m/Assistant/contracts/identity"
|
|
"git.cin.su/m/Assistant/contracts/interaction"
|
|
"git.cin.su/m/Assistant/platform/ids"
|
|
)
|
|
|
|
const MaxHumanUtteranceBytes = 16 << 10
|
|
|
|
var (
|
|
ErrEmptyMessage = errors.New("empty human message")
|
|
ErrMessageTooLarge = errors.New("human message too large")
|
|
ErrInvalidPrincipal = errors.New("invalid interaction principal")
|
|
)
|
|
|
|
type Clock func() time.Time
|
|
|
|
func NewConversation(actorID identity.ActorID, principalID identity.PrincipalID, now time.Time) (interaction.Conversation, error) {
|
|
if actorID == "" || principalID == "" {
|
|
return interaction.Conversation{}, ErrInvalidPrincipal
|
|
}
|
|
id, err := ids.New()
|
|
if err != nil {
|
|
return interaction.Conversation{}, err
|
|
}
|
|
millis := now.UnixMilli()
|
|
return interaction.Conversation{
|
|
ID: interaction.ConversationID(id),
|
|
ActorID: actorID,
|
|
PrincipalID: principalID,
|
|
Channel: interaction.ChannelWebChat,
|
|
State: interaction.ConversationOpen,
|
|
CreatedAtUnixMillis: millis,
|
|
UpdatedAtUnixMillis: millis,
|
|
}, nil
|
|
}
|
|
|
|
func NewHumanUtterance(conversation interaction.Conversation, request interaction.ChatMessageRequest, now time.Time) (interaction.HumanUtterance, interaction.OriginalHumanIntent, []interaction.InteractionRelation, error) {
|
|
message := strings.TrimSpace(request.Message)
|
|
if message == "" {
|
|
return interaction.HumanUtterance{}, interaction.OriginalHumanIntent{}, nil, ErrEmptyMessage
|
|
}
|
|
if len(message) > MaxHumanUtteranceBytes || !utf8.ValidString(message) {
|
|
return interaction.HumanUtterance{}, interaction.OriginalHumanIntent{}, nil, ErrMessageTooLarge
|
|
}
|
|
utteranceID, err := ids.New()
|
|
if err != nil {
|
|
return interaction.HumanUtterance{}, interaction.OriginalHumanIntent{}, nil, err
|
|
}
|
|
intentID, err := ids.New()
|
|
if err != nil {
|
|
return interaction.HumanUtterance{}, interaction.OriginalHumanIntent{}, nil, err
|
|
}
|
|
millis := now.UnixMilli()
|
|
speechAct := ClassifySpeechAct(message, request.CorrectsUtteranceID)
|
|
utterance := interaction.HumanUtterance{
|
|
ID: interaction.HumanUtteranceID(utteranceID),
|
|
ConversationID: conversation.ID,
|
|
ActorID: conversation.ActorID,
|
|
PrincipalID: conversation.PrincipalID,
|
|
Channel: interaction.ChannelWebChat,
|
|
ReceivedAtUnixMillis: millis,
|
|
CorrelationID: request.CorrelationID,
|
|
Content: message,
|
|
ContentRef: "interaction:utterance:" + string(utteranceID),
|
|
SpeechAct: speechAct,
|
|
InterpretationState: interaction.InterpretationInterpreted,
|
|
CorrectsUtteranceID: request.CorrectsUtteranceID,
|
|
}
|
|
intent := interaction.OriginalHumanIntent{
|
|
ID: interaction.OriginalHumanIntentID(intentID),
|
|
UtteranceID: utterance.ID,
|
|
RecordedAtUnixMillis: millis,
|
|
Text: message,
|
|
}
|
|
var relations []interaction.InteractionRelation
|
|
if request.CorrectsUtteranceID != "" || speechAct == interaction.SpeechActCorrection {
|
|
relationID, err := ids.New()
|
|
if err != nil {
|
|
return interaction.HumanUtterance{}, interaction.OriginalHumanIntent{}, nil, err
|
|
}
|
|
relations = append(relations, interaction.InteractionRelation{
|
|
ID: interaction.InteractionRelationID(relationID),
|
|
FromUtteranceID: utterance.ID,
|
|
ToUtteranceID: request.CorrectsUtteranceID,
|
|
Kind: interaction.RelationCorrects,
|
|
RecordedAtUnixMillis: millis,
|
|
})
|
|
}
|
|
return utterance, intent, relations, nil
|
|
}
|
|
|
|
func ClassifySpeechAct(message string, corrects interaction.HumanUtteranceID) interaction.SpeechAct {
|
|
lower := strings.ToLower(strings.TrimSpace(message))
|
|
switch {
|
|
case corrects != "" || strings.Contains(lower, "исправ") || strings.Contains(lower, "не так") || strings.HasPrefix(lower, "нет,"):
|
|
return interaction.SpeechActCorrection
|
|
case strings.Contains(lower, "?") || strings.HasPrefix(lower, "что ") || strings.HasPrefix(lower, "как ") || strings.HasPrefix(lower, "почему ") || strings.HasPrefix(lower, "кто ") || strings.Contains(lower, "объясни"):
|
|
return interaction.SpeechActQuestion
|
|
case strings.Contains(lower, "проверь") || strings.Contains(lower, "исслед") || strings.Contains(lower, "разбер"):
|
|
return interaction.SpeechActRequestToInvestigate
|
|
case strings.Contains(lower, "сделай") || strings.Contains(lower, "запусти") || strings.Contains(lower, "создай") || strings.Contains(lower, "настрой") || strings.Contains(lower, "добавь") || strings.Contains(lower, "измени") || strings.Contains(lower, "поменяй") || strings.Contains(lower, "используй") || strings.Contains(lower, "удали") || strings.Contains(lower, "удалить") || strings.Contains(lower, "напомни") || strings.Contains(lower, "подключи"):
|
|
return interaction.SpeechActDirective
|
|
case strings.HasPrefix(lower, "я решил") || strings.Contains(lower, "решение"):
|
|
return interaction.SpeechActDecision
|
|
case strings.Contains(lower, "предпочита") || strings.Contains(lower, "лучше"):
|
|
return interaction.SpeechActPreference
|
|
case strings.HasPrefix(lower, "думаю") || strings.Contains(lower, "гипотез"):
|
|
return interaction.SpeechActHypothesis
|
|
case lower == "да" || strings.HasPrefix(lower, "да,") || strings.Contains(lower, "подтверж"):
|
|
return interaction.SpeechActConfirmation
|
|
case lower == "нет" || strings.HasPrefix(lower, "нет,") || strings.Contains(lower, "отмен"):
|
|
return interaction.SpeechActRejection
|
|
case strings.Contains(lower, "привет") || strings.Contains(lower, "здравств"):
|
|
return interaction.SpeechActConversational
|
|
default:
|
|
return interaction.SpeechActAssertion
|
|
}
|
|
}
|
|
|
|
func NeedsCognition(act interaction.SpeechAct) bool {
|
|
return act == interaction.SpeechActQuestion || act == interaction.SpeechActRequestToInvestigate
|
|
}
|
|
|
|
func FallbackText(act interaction.SpeechAct) string {
|
|
switch act {
|
|
case interaction.SpeechActDirective:
|
|
return "Я понял действие как просьбу, но без отдельного Authority/Commit шага не выполняю мутацию."
|
|
case interaction.SpeechActAssertion:
|
|
return "Я записал это как утверждение человека. Это не считается проверенным фактом."
|
|
case interaction.SpeechActCorrection:
|
|
return "Исправление записано отдельной репликой; история не переписана."
|
|
case interaction.SpeechActQuestion, interaction.SpeechActRequestToInvestigate:
|
|
return "Когнитивный маршрут сейчас недоступен, но сообщение сохранено и может быть обработано позже."
|
|
default:
|
|
return "Сообщение записано."
|
|
}
|
|
}
|