neocortex

package module
v0.0.0-...-d110d06 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 10, 2020 License: MIT Imports: 21 Imported by: 0

README

Neocortex 🧠

Neocortex is a tool to connect your cognitive service with your web services and communication channels.

The main goal of neocortex is offers a reliable and modern API to connect any kind of cognitive service* with any communication channel**.

*Currently neocortex offers only two cognitive services: Watson and a simple Useless box as dummy service, you can collaborate to implement another cognitive service like DialogFlow or Amazon Lex, later I'm going to document how to implement this services but you can read the source code to understand how to.

**Like cognitive services, I could only implement only two channels: Facebook Messenger and a simple Terminal chat (very simple to emulate a chat in your terminal), if you want you can collaborate implementing other channels like Slack, Whatsapp or Gmail, for example.

🚧 Neocortex is work in progress, it pretends to be a big collaborative project

TODO
  • Think the paradigm and write the first types of neocortex.

  • Describe a Cognitive Service interface (type CognitiveService interface)

  • Describe a Communication channel interface (type CommunicationChannel interface)

  • Implement the Watson cognitive service

  • Implement the Facebook channel

  • Implement persistence contexts and dialog sessions

  • Implement analytics reports

  • MongoDB repository driver

  • Write unit tests // working on

  • Make an iteration of the Communication channel's architecture

  • Think more in the Cognitive's orientation (paradigm, architecture, etc)

  • Improve the neocortex engine

  • Write the Gmail channel implementation

  • Write the Dialog flow service implementation

  • Improve facebook messenger API

  • Document more

  • Document more more!

Install

Install with:

go get -u github.com/bregydoc/neocortex

Currently, neocortex has 2 implementations of Cognitive Services (Useless-box created by me and Watson Assistant based on the watson official API v2) and 2 implementations of Communication Channels (Terminal based UI written by me and Facebook Messenger forked from Facebook-messenger API by mileusna).

Basic Example
package main

import (
	neo "github.com/bregydoc/neocortex"
	"github.com/bregydoc/neocortex/channels/terminal"
	"github.com/bregydoc/neocortex/cognitive/uselessbox"
	"github.com/bregydoc/neocortex/repositories/boltdb"
)

// Example of use useless box with terminal channel
func main() {
	box := uselessbox.NewCognitive()
	term := terminal.NewChannel(nil)

	repo, err  := boltdb.New("neocortex.db")
	if err != nil {
		panic(err)
	}
	
	engine, err := neo.New(repo, box, term)
	if err != nil {
		panic(err)
	}

	engine.ResolveAny(term, func(in *neo.Input, out *neo.Output, response neo.OutputResponse) error {
		out.AddTextResponse("-----Watermark-----")
		return response(out)
	})

	if err = engine.Run(); err != nil {
		panic(err)
	}
}

You can see more examples here.

Paradigm

Neocortex is like a middleware with a mux, with it you can catch your message input, pass to your cognitive service, inflate or modify them and respond.

Concepts
  1. Cognitive Service

    Represents any service that decodes and find intents and entities in a human message. In neocortex, this is described by a simple interface.

    type CognitiveService interface {
       CreateNewContext(c *context.Context, info neocortex.PersonInfo) *neocortex.Context
       GetProtoResponse(in *neocortex.Input) (*neocortex.Output, error)
    }
    

    you can see the implementation of a Useless box or Watson Assistant.

  2. Communication Channel

    A Communication Channel is any human interface where a person can to send a message and receive a response. Currently, I think we need to work more in the paradigm behind Communication channels. In neocortex a communication channel is described by the following interface:

    type CommunicationChannel interface {
       RegisterMessageEndpoint(handler neocortex.MiddleHandler) error
       ToHear() error
       GetContextFabric() neocortex.ContextFabric
       SetContextFabric(fabric neocortex.ContextFabric)
       OnNewContextCreated(callback func(c *neocortex.Context))
    }
    

    Please, read how to are implemented the Terminal channel or Facebook Messenger Channel.

  3. Context

    Neocortex's Context represents a "session" or "dialog" with a human, it contains essential information about the person with we're a conversation.

    // Context represent the context of one conversation
    type Context struct {
       Context   *context.Context // go native context implementation
       SessionID string           
       Person    neocortex.PersonInfo             
       Variables map[string]interface{} //conversation context variables
    }
    
  4. Input

    An input is a message input, that's all. Input has a specified type and in the neocortex is a struct:

    type Input struct {
       Context  *neocortex.Context
       Data     neocortex.InputData
       Entities []neocortex.Entity
       Intents  []neocortex.Intent
    }
    

    Intents and Entities define the message.

  5. Output

    An output represents a response in a conversation, with this you can define the response to your communication channel (e.g. facebook messenger) and if your channel allows you can respond different types of response (e.g. Image, Audio, Attachment, etc).

    type Output struct {
       Context      *neocortex.Context
       Entities     []neocortex.Entity
       Intents      []neocortex.Intent
       VisitedNodes []*neocortex.DialogNode
       Logs         []*neocortex.LogMessage
       Responses    []neocortex.Response // A list of responses
    }
    
    type Response struct {
        IsTyping bool
        Type     neocortex.ResponseType
        Value    interface{}
    }
    
  6. Engine

    This is the core of neocortex it can to connect and manage your Cognitive service and your Communication channels. the engine has different methods for intercept a message and modifies it.

    // Create a New neocortex Engine
    func New(cognitive CognitiveService, channels ...CommunicationChannel) (*Engine, error) {}
    
    // Register a new resolver, you need a matcher
    func (engine *Engine) Resolve(channel CommunicationChannel, matcher Matcher, handler HandleResolver) {}
    
    // Maatcher
    type Matcher struct {
        Entity Match
        Intent Match
        AND    *Matcher
        OR     *Matcher
    }
    // Match
    type Match struct {
        Is         string
        Confidence float64
    }
    
  7. Resolver

    This is the core of the neocortex paradigm, with this you can intercept a message and modify or only bypass it. You need to pass a Matcher who is used to match with your message inputs, you can see below how looks like a Matcher struct. Above you can see two examples of a matcher:

    // match if the input has a Regard or Goodbye intents 
    match := neo.Matcher{Intent: neo.Match{Is: "REGARD"}, OR: &neo.Matcher{Intent: neo.Match{Is: "GOODBYE"}}}
    
    // match if the input is an insult intent and has a bad_word entity
    match := neo.Matcher{
     Intent: neo.Match{Is:"INSULT", Confidence: 0.8}, 
     AND: &neo.Matcher{
       Entity: neo.Match{
         Is: "bad_word",
       },
     },
    }
    

    Register a new resolver is simple, see the following example above

    match := neo.Matcher{Intent: neo.Match{Is:"HELLO", Confidence: 0.8}}
    engine.Resolve(fb, match, func(in *neo.Input, out *neo.Output, response neo.OutputResponse) error {
      out.AddTextResponse("Powered by neocortex")
      return response(out)
    })
    

    You can make another type of Resolves.

    func (engine *Engine) ResolveAny(channel CommunicationChannel, handler HandleResolver) {}
    func (engine *Engine) Resolve(channel CommunicationChannel, matcher Matcher, handler HandleResolver) {}
    func (engine *Engine) ResolveMany(channels []CommunicationChannel, matcher Matcher, handler HandleResolver) {}
    func (engine *Engine) ResolveManyAny(channels []CommunicationChannel, handler HandleResolver) {}
    

🚧 Work in progress documentation, if you want to help, only send me an email.

love open source

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrChannelIsNotRegistered = errors.New("channel not exist on this engine instance")
View Source
var ErrContextNotExist = errors.New("context is not valid or not exist")
View Source
var ErrInvalidInputType = errors.New("invalid or unimplemented input type")
View Source
var ErrInvalidResponseFromCognitiveService = errors.New("invalid response from cognitive service")

var ErrSessionExpired = errors.Default("session expired")

View Source
var ErrSessionNotExist = errors.New("session not exist")

Functions

This section is empty.

Types

type API

type API struct {
	Port string
	// contains filtered or unexported fields
}

func (*API) Launch

func (api *API) Launch(engine *Engine) error

type ActionVarType

type ActionVarType string
const TextActionVar ActionVarType = "text"

type ActionVariable

type ActionVariable struct {
	Name  string        `json:"name"`
	Type  ActionVarType `json:"type"`
	Value []byte        `json:"value"`
}

type Analytics

type Analytics struct {
	// contains filtered or unexported fields
}

type Bot

type Bot struct {
	Name    string `json:"name"`
	Version string `json:"version"`
	Author  string `json:"author"`
}

type CMatch

type CMatch struct {
	Name  string
	Value interface{}
}

type Chat

type Chat struct {
	ID            string     `json:"id"`
	LastMessageAt time.Time  `json:"last_message_at"`
	Person        PersonInfo `json:"person"`
	Performance   float64    `json:"performance"`
	Messages      []Message  `json:"messages"`
}

type CognitiveService

type CognitiveService interface {
	CreateNewContext(c *context.Context, info PersonInfo) *Context
	OnContextIsDone(callback func(c *Context))
	GetProtoResponse(c *Context, in *Input) (*Output, error)
}

type CommunicationChannel

type CommunicationChannel interface {
	RegisterMessageEndpoint(handler MiddleHandler) error
	ToHear() error
	GetContextFabric() ContextFabric // TODO: Rev
	SetContextFabric(fabric ContextFabric)
	OnNewContextCreated(callback func(c *Context))
	OnContextIsDone(callback func(c *Context))
	CallContextDone(c *Context)
}

type Configuration

type Configuration struct {
}

type Context

type Context struct {
	Context   *context.Context       `json:"-" bson:"-"`
	SessionID string                 `json:"session_id" bson:"session_id"`
	Person    PersonInfo             `json:"person" bson:"person"`
	Variables map[string]interface{} `json:"variables" bson:"variables"`
}

Context represent the context of a conversation

func (*Context) DeleteContextVariable

func (c *Context) DeleteContextVariable(name string, value interface{})

func (*Context) GetFloat64ContextVariable

func (c *Context) GetFloat64ContextVariable(name string, _default float64) float64

func (*Context) GetIntContextVariable

func (c *Context) GetIntContextVariable(name string, _default int) int

func (*Context) GetStringContextVariable

func (c *Context) GetStringContextVariable(name string, _default string) string

func (*Context) SetContextVariable

func (c *Context) SetContextVariable(name string, value interface{})

func (*Context) String

func (c *Context) String() string

type ContextFabric

type ContextFabric func(ctx context.Context, info PersonInfo) *Context

type ContextRecord

type ContextRecord struct {
	At      time.Time `json:"at"`
	Context Context   `json:"context"`
}

type Dialog

type Dialog struct {
	ID           string           `json:"id" bson:"id"`
	StartAt      time.Time        `json:"start_at" bson:"start_at"`
	LastActivity time.Time        `json:"last_activity" bson:"last_activity"`
	EndAt        time.Time        `json:"end_at" bson:"end_at"`
	Ins          []*InputRecord   `json:"ins" bson:"ins"`
	Outs         []*OutputRecord  `json:"outs" bson:"outs"`
	Contexts     []*ContextRecord `json:"contexts" bson:"contexts"`
	Performance  float64          `json:"performance" bson:"performance"`
}

func (*Dialog) HasContextVar

func (dialog *Dialog) HasContextVar(contextVar string) bool

func (*Dialog) HasDialogNode

func (dialog *Dialog) HasDialogNode(node string) bool

func (*Dialog) HasEntity

func (dialog *Dialog) HasEntity(entity string) bool

TODO: To optimize, please find all at the same time (that has better performance)

func (*Dialog) HasIntent

func (dialog *Dialog) HasIntent(intent string) bool

type DialogNode

type DialogNode struct {
	Name       string `json:"name"`
	Title      string `json:"title"`
	Conditions string `json:"conditions"`
}

DialogNode represents a node into complex tree of any dialog

type DialogNodeMatch

type DialogNodeMatch struct {
	Title string
	Name  string
}

type Engine

type Engine struct {
	Repository    Repository
	ActiveDialogs map[*Context]*Dialog

	Register map[string]string

	Analytics *Analytics
	// contains filtered or unexported fields
}

func Default

func Default(repository Repository, cognitive CognitiveService, channels ...CommunicationChannel) (*Engine, error)

Default ...

func (*Engine) Inject

func (engine *Engine) Inject(channel CommunicationChannel, matcher *Matcher, middle InInjection)

func (*Engine) InjectAll

func (engine *Engine) InjectAll(channel CommunicationChannel, middle InInjection)

func (*Engine) RegisterAdmin

func (engine *Engine) RegisterAdmin(Username, Password string) error

RegisterAdmin you can register new admin for get info purpose

func (*Engine) RemoveAdmin

func (engine *Engine) RemoveAdmin(Username string) error

RemoveAdmin let you remove the admin

func (*Engine) Resolve

func (engine *Engine) Resolve(channel CommunicationChannel, matcher *Matcher, handler HandleResolver)

func (*Engine) ResolveAny

func (engine *Engine) ResolveAny(channel CommunicationChannel, handler HandleResolver)

func (*Engine) ResolveMany

func (engine *Engine) ResolveMany(channels []CommunicationChannel, matcher *Matcher, handler HandleResolver)

func (*Engine) ResolveManyAny

func (engine *Engine) ResolveManyAny(channels []CommunicationChannel, handler HandleResolver)

func (*Engine) Run

func (engine *Engine) Run() error

func (*Engine) SetPerformanceMetric

func (engine *Engine) SetPerformanceMetric(perf func(*Dialog) float64)

SetPerformanceMetric links a custom performance metric function

type Entity

type Entity struct {
	Entity     string                 `json:"entity"`
	Location   []int64                `json:"location"`
	Value      string                 `json:"value"`
	Confidence float64                `json:"confidence"`
	Metadata   map[string]interface{} `json:"metadata"`
}

Entity define any kind of object or entity

type HandleResolver

type HandleResolver func(c *Context, in *Input, out *Output, response OutputResponse) error

type InInjection

type InInjection func(c *Context, in *Input) *Input

type Input

type Input struct {
	Data     InputData `json:"data"`
	Entities []Entity  `json:"entities"`
	Intents  []Intent  `json:"intents"`
}

Input represent an Input for the cognitive service

func (*Input) Match

func (in *Input) Match(c *Context, matcher *Matcher) bool

type InputData

type InputData struct {
	Type  InputType `json:"type"`
	Value string    `json:"value"`
	Data  []byte    `json:"data"`
}

type InputRecord

type InputRecord struct {
	At    time.Time `json:"at"`
	Input Input     `json:"input"`
}

type InputType

type InputType string
const InputAudio InputType = "audio"
const InputEmoji InputType = "emoji"
const InputImage InputType = "image"
const InputText InputType = "text"

type Intent

type Intent struct {
	Intent     string  `json:"intent"`
	Confidence float64 `json:"confidence"`
}

Intent define any intent, and intent is like a wish, an intention

type LogLevelType

type LogLevelType string

LogLevelType is level of the logs

var Error LogLevelType = "error"

Error is a level of log

var Info LogLevelType = "info"

Info is a level of log

var Warn LogLevelType = "warn"

Warn is a level of log

type LogMessage

type LogMessage struct {
	Level   LogLevelType `json:"level"`
	Message string       `json:"message"`
}

LogMessage represents a snapshot of the messages around the dialog

type Match

type Match struct {
	Is         string
	Confidence float64
}

type Matcher

type Matcher struct {
	DialogNode      DialogNodeMatch
	Entity          Match
	Intent          Match
	ContextVariable CMatch
	AND             *Matcher
	OR              *Matcher
}

func IfContextVariableIs

func IfContextVariableIs(name string, value interface{}) *Matcher

func IfDialogNodeIs

func IfDialogNodeIs(title, name string) *Matcher

func IfDialogNodeNameIs

func IfDialogNodeNameIs(name string) *Matcher

func IfDialogNodeTitleIs

func IfDialogNodeTitleIs(title string) *Matcher

func IfEntityIs

func IfEntityIs(entity string, confidence ...float64) *Matcher

func IntentIs

func IntentIs(intent string, confidence ...float64) *Matcher

func (*Matcher) And

func (m *Matcher) And(and *Matcher) *Matcher

func (*Matcher) AndEntityIs

func (m *Matcher) AndEntityIs(entity string, confidence ...float64) *Matcher

func (*Matcher) AndIfContextVariableIs

func (m *Matcher) AndIfContextVariableIs(name string, value interface{}) *Matcher

func (*Matcher) AndIfDialogNodeIs

func (m *Matcher) AndIfDialogNodeIs(title, name string) *Matcher

func (*Matcher) AndIfDialogNodeNameIs

func (m *Matcher) AndIfDialogNodeNameIs(name string) *Matcher

func (*Matcher) AndIfDialogNodeTitleIs

func (m *Matcher) AndIfDialogNodeTitleIs(title string) *Matcher

func (*Matcher) AndIntentIs

func (m *Matcher) AndIntentIs(intent string, confidence ...float64) *Matcher

func (*Matcher) Or

func (m *Matcher) Or(or *Matcher) *Matcher

func (*Matcher) OrEntityIs

func (m *Matcher) OrEntityIs(entity string, confidence ...float64) *Matcher

func (*Matcher) OrIfContextVariableIs

func (m *Matcher) OrIfContextVariableIs(name string, value interface{}) *Matcher

func (*Matcher) OrIfDialogNodeIs

func (m *Matcher) OrIfDialogNodeIs(title, name string) *Matcher

func (*Matcher) OrIfDialogNodeNameIs

func (m *Matcher) OrIfDialogNodeNameIs(name string) *Matcher

func (*Matcher) OrIfDialogNodeTitleIs

func (m *Matcher) OrIfDialogNodeTitleIs(title string) *Matcher

func (*Matcher) OrIntentIs

func (m *Matcher) OrIntentIs(intent string, confidence ...float64) *Matcher

type Message

type Message struct {
	At       time.Time       `json:"at"`
	Owner    MessageOwner    `json:"owner"`
	Intents  []Intent        `json:"intents"`
	Entities []Entity        `json:"entities"`
	Response MessageResponse `json:"response"`
}

type MessageOwner

type MessageOwner string
const ChatBot MessageOwner = "bot"
const Person MessageOwner = "person"

type MessageResponse

type MessageResponse struct {
	Type  ResponseType `json:"type"`
	Value interface{}  `json:"value"`
}

type MiddleHandler

type MiddleHandler func(c *Context, message *Input, response OutputResponse) error

type Option

type Option struct {
	Text       string `json:"text"`
	Action     string `json:"action"`
	IsPostBack bool   `json:"is_post_back"`
}

type OptionsResponse

type OptionsResponse struct {
	Title       string    `json:"title"`
	Description string    `json:"description"`
	ItemURL     string    `json:"item_url"`
	Image       string    `json:"image"`
	Options     []*Option `json:"options"`
}

type Output

type Output struct {
	Entities     []Entity      `json:"entities"`
	Intents      []Intent      `json:"intents"`
	VisitedNodes []*DialogNode `json:"visited_nodes"`
	Logs         []*LogMessage `json:"logs"`
	Responses    []Response    `json:"responses"`
}

Output represents the response of an input from the cognitive service

func (*Output) AddImageResponse

func (out *Output) AddImageResponse(url string) *Output

func (*Output) AddListOfOptionsResponse

func (out *Output) AddListOfOptionsResponse(options []OptionsResponse) *Output

func (*Output) AddOptionsResponse

func (out *Output) AddOptionsResponse(title string, subtitle string, options ...Option) *Output

func (*Output) AddPauseResponse

func (out *Output) AddPauseResponse(duration time.Duration) *Output

func (*Output) AddTextResponse

func (out *Output) AddTextResponse(resp string) *Output

func (*Output) Clean

func (out *Output) Clean() *Output

func (*Output) ClearResponses

func (out *Output) ClearResponses() *Output

func (*Output) Fill

func (out *Output) Fill(data interface{}) *Output

func (*Output) Match

func (out *Output) Match(c *Context, matcher *Matcher) bool

type OutputRecord

type OutputRecord struct {
	At     time.Time `json:"at"`
	Output Output    `json:"output"`
}

type OutputResponse

type OutputResponse func(c *Context, output *Output) error

type PersonInfo

type PersonInfo struct {
	ID       string `json:"id"`
	Timezone string `json:"timezone"`
	Picture  string `json:"picture"`
	Locale   string `json:"locale"`
	Name     string `json:"name"`
}

PersonInfo describes the basic info of the person

type Pipeline

type Pipeline struct {
	// contains filtered or unexported fields
}

type Repository

type Repository interface {
	SaveDialog(dialog *Dialog) error
	GetDialogByID(id string) (*Dialog, error)
	AllDialogs(frame TimeFrame) ([]*Dialog, error) // to page or not to page?
	DeleteDialog(id string) (*Dialog, error)

	// Dialogs are inmutable, cause it doesn't have an updater
	DialogsByView(viewID string, frame TimeFrame) ([]*Dialog, error)
	Summary(frame TimeFrame) (*Summary, error)

	RegisterIntent(intent string) error
	RegisterEntity(entity string) error
	RegisterDialogNode(name string) error
	RegisterContextVar(value string) error
	Intents() []string
	Entities() []string
	DialogNodes() []string
	ContextVars() []string

	SaveView(view *View) error
	GetViewByID(id string) (*View, error)
	FindViewByName(name string) ([]*View, error)
	AllViews() ([]*View, error)
	UpdateView(view *View) error
	DeleteView(id string) (*View, error)

	SetActionVar(name string, value string) error
	GetActionVar(name string) (string, error)
}

type Response

type Response struct {
	IsTyping bool         `json:"is_typing"`
	Type     ResponseType `json:"type"`
	Value    interface{}  `json:"value"`
}

type ResponseType

type ResponseType string

ResponseType define the types of generic response

const Image ResponseType = "image"

Image is a kind of generic response

const Options ResponseType = "option"

Options is a kind of generic response

const Pause ResponseType = "pause"

Pause is a kind of generic response

const Suggestion ResponseType = "suggestion"
const Text ResponseType = "text"

Text is a kind of generic response

const Unknown ResponseType = "unknown"

type Summary

type Summary struct {
	TotalDialogs    int64                   `json:"total_dialogs"`
	TotalUsers      int64                   `json:"total_users"`
	RecurrentUsers  int64                   `json:"recurrent_users"`
	UsersByTimezone map[string]UsersSummary `json:"users_by_timezone"`
	PerformanceMean float64                 `json:"performance_mean"`
}

type TimeAnalysisResult

type TimeAnalysisResult struct {
	Timeline     map[time.Time]map[string]float64 `json:"timeline"`
	TotalDialogs int                              `json:"total_dialogs"`
	TotalCounts  int                              `json:"total_counts"`
	Individuals  map[string]float64               `json:"individuals"`
}

type TimeFrame

type TimeFrame struct {
	From     time.Time
	To       time.Time
	Preset   TimeFramePreset
	PageSize int
	PageNum  int
}

type TimeFramePreset

type TimeFramePreset string
const DayPreset TimeFramePreset = "day"
const MonthPreset TimeFramePreset = "month"
const WeekPreset TimeFramePreset = "week"
const YearPreset TimeFramePreset = "year"

type UsersSummary

type UsersSummary struct {
	News       int64 `json:"news"`
	Recurrents int64 `json:"recurrents"`
}

type View

type View struct {
	ID            string      `json:"id"`
	Name          string      `json:"name"`
	FrequencyMode bool        `json:"frequency_mode"`
	Styles        []ViewStyle `json:"styles"`
	Classes       []ViewClass `json:"classes"`
	Children      []*View     `json:"children"`
}

type ViewClass

type ViewClass struct {
	Type  ViewClassType `json:"type"`
	Value string        `json:"value"`
}

type ViewClassType

type ViewClassType string
const ContextVarClass ViewClassType = "context_var"
const DialogNodeClass ViewClassType = "node"
const EntityClass ViewClassType = "entity"
const IntentClass ViewClassType = "intent"

type ViewStyle

type ViewStyle string
const Bars ViewStyle = "bars"
const Line ViewStyle = "line"
const Pie ViewStyle = "pie"

Directories

Path Synopsis
channels
cognitive
examples
repositories

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL