package postgres import ( "context" "git.cin.su/m/Assistant/contracts/interaction" "github.com/jackc/pgx/v5/pgxpool" ) type InteractionRepository struct{ pool *pgxpool.Pool } func OpenInteraction(ctx context.Context, c ContinuityConfig) (*InteractionRepository, error) { p, e := openPool(ctx, c) return &InteractionRepository{p}, e } func (r *InteractionRepository) Close() { r.pool.Close() } func (r *InteractionRepository) CreateConversation(ctx context.Context, c interaction.Conversation) error { _, err := r.pool.Exec(ctx, `INSERT INTO interaction.conversations(conversation_id,actor_id,principal_id,channel,state,created_at_unix_millis,updated_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7) ON CONFLICT(conversation_id) DO NOTHING`, c.ID, c.ActorID, c.PrincipalID, c.Channel, c.State, c.CreatedAtUnixMillis, c.UpdatedAtUnixMillis) return err } func (r *InteractionRepository) AppendUtterance(ctx context.Context, c interaction.Conversation, u interaction.HumanUtterance, intent interaction.OriginalHumanIntent, relations []interaction.InteractionRelation) error { tx, err := r.pool.Begin(ctx) if err != nil { return err } defer tx.Rollback(ctx) if _, err = tx.Exec(ctx, `INSERT INTO interaction.conversations(conversation_id,actor_id,principal_id,channel,state,created_at_unix_millis,updated_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7) ON CONFLICT(conversation_id) DO UPDATE SET updated_at_unix_millis=EXCLUDED.updated_at_unix_millis`, c.ID, c.ActorID, c.PrincipalID, c.Channel, c.State, c.CreatedAtUnixMillis, c.UpdatedAtUnixMillis); err != nil { return err } if _, err = tx.Exec(ctx, `INSERT INTO interaction.utterances(utterance_id,conversation_id,actor_id,principal_id,channel,received_at_unix_millis,correlation_id,content,content_ref,speech_act,interpretation_state,corrects_utterance_id) VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12)`, u.ID, u.ConversationID, u.ActorID, u.PrincipalID, u.Channel, u.ReceivedAtUnixMillis, u.CorrelationID, u.Content, u.ContentRef, u.SpeechAct, u.InterpretationState, u.CorrectsUtteranceID); err != nil { return err } if _, err = tx.Exec(ctx, `INSERT INTO interaction.original_human_intents(intent_id,utterance_id,recorded_at_unix_millis,text) VALUES($1,$2,$3,$4)`, intent.ID, intent.UtteranceID, intent.RecordedAtUnixMillis, intent.Text); err != nil { return err } for _, relation := range relations { if _, err = tx.Exec(ctx, `INSERT INTO interaction.relations(relation_id,from_utterance_id,to_utterance_id,kind,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5)`, relation.ID, relation.FromUtteranceID, relation.ToUtteranceID, relation.Kind, relation.RecordedAtUnixMillis); err != nil { return err } } return tx.Commit(ctx) } func (r *InteractionRepository) SaveSemanticResult(ctx context.Context, interpretation interaction.UtteranceInterpretation, candidates []interaction.SemanticCandidate, projects []interaction.ProjectReference, relations []interaction.InteractionRelation) error { tx, err := r.pool.Begin(ctx) if err != nil { return err } defer tx.Rollback(ctx) if _, err = tx.Exec(ctx, `INSERT INTO interaction.utterance_interpretations(interpretation_id,utterance_id,speech_act,secondary_speech_act,confidence_millis,source,assertion_verification,requested_outcome,clarification,needs_cognition,applicability,selection_reason,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)`, interpretation.ID, interpretation.UtteranceID, interpretation.SpeechAct, interpretation.SecondarySpeechAct, interpretation.ConfidenceMillis, interpretation.Source, interpretation.AssertionVerification, interpretation.RequestedOutcome, interpretation.Clarification, interpretation.NeedsCognition, interpretation.Applicability, interpretation.SelectionReason, interpretation.RecordedAtUnixMillis); err != nil { return err } for _, candidate := range candidates { if _, err = tx.Exec(ctx, `INSERT INTO interaction.semantic_candidates(candidate_id,utterance_id,source,selection_state,primary_speech_act,secondary_speech_act,confidence_millis,applicability,requested_outcome,clarification,needs_cognition,selection_reason,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13) ON CONFLICT(candidate_id) DO UPDATE SET selection_state=EXCLUDED.selection_state,primary_speech_act=EXCLUDED.primary_speech_act,secondary_speech_act=EXCLUDED.secondary_speech_act,confidence_millis=EXCLUDED.confidence_millis,applicability=EXCLUDED.applicability,requested_outcome=EXCLUDED.requested_outcome,clarification=EXCLUDED.clarification,needs_cognition=EXCLUDED.needs_cognition,selection_reason=EXCLUDED.selection_reason,recorded_at_unix_millis=EXCLUDED.recorded_at_unix_millis`, candidate.ID, candidate.UtteranceID, candidate.Source, candidate.SelectionState, candidate.PrimarySpeechAct, candidate.SecondarySpeechAct, candidate.ConfidenceMillis, candidate.Applicability, candidate.RequestedOutcome, candidate.Clarification, candidate.NeedsCognition, candidate.SelectionReason, candidate.RecordedAtUnixMillis); err != nil { return err } for _, hint := range candidate.CapabilityHints { if _, err = tx.Exec(ctx, `INSERT INTO interaction.semantic_candidate_capability_hints(candidate_id,capability_kind) VALUES($1,$2) ON CONFLICT DO NOTHING`, candidate.ID, hint); err != nil { return err } } } for _, project := range projects { if _, err = tx.Exec(ctx, `INSERT INTO interaction.project_references(project_reference_id,utterance_id,project_id,project_display_name,role,provenance,confidence_millis,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5,$6,$7,$8)`, project.ID, project.UtteranceID, project.ProjectID, project.ProjectDisplayName, project.Role, project.Provenance, project.ConfidenceMillis, project.RecordedAtUnixMillis); err != nil { return err } } for _, relation := range relations { if relation.ToUtteranceID == "" { continue } if _, err = tx.Exec(ctx, `INSERT INTO interaction.relations(relation_id,from_utterance_id,to_utterance_id,kind,recorded_at_unix_millis) VALUES($1,$2,$3,$4,$5) ON CONFLICT(relation_id) DO NOTHING`, relation.ID, relation.FromUtteranceID, relation.ToUtteranceID, relation.Kind, relation.RecordedAtUnixMillis); err != nil { return err } } return tx.Commit(ctx) } func (r *InteractionRepository) Conversation(ctx context.Context, id interaction.ConversationID) (interaction.Conversation, error) { var c interaction.Conversation e := r.pool.QueryRow(ctx, `SELECT conversation_id,actor_id,principal_id,channel,state,created_at_unix_millis,updated_at_unix_millis FROM interaction.conversations WHERE conversation_id=$1`, id).Scan(&c.ID, &c.ActorID, &c.PrincipalID, &c.Channel, &c.State, &c.CreatedAtUnixMillis, &c.UpdatedAtUnixMillis) return c, e } func (r *InteractionRepository) Utterances(ctx context.Context, id interaction.ConversationID) ([]interaction.HumanUtterance, error) { rows, e := r.pool.Query(ctx, `SELECT utterance_id,conversation_id,actor_id,principal_id,channel,received_at_unix_millis,correlation_id,content,content_ref,speech_act,interpretation_state,corrects_utterance_id FROM interaction.utterances WHERE conversation_id=$1 ORDER BY received_at_unix_millis,utterance_id`, id) if e != nil { return nil, e } defer rows.Close() var out []interaction.HumanUtterance for rows.Next() { var u interaction.HumanUtterance if e = rows.Scan(&u.ID, &u.ConversationID, &u.ActorID, &u.PrincipalID, &u.Channel, &u.ReceivedAtUnixMillis, &u.CorrelationID, &u.Content, &u.ContentRef, &u.SpeechAct, &u.InterpretationState, &u.CorrectsUtteranceID); e != nil { return nil, e } out = append(out, u) } return out, rows.Err() }