Pipelines Lua
O que é?
O sistema de Pipelines permite criar interações conversacionais customizadas através de scripts em Lua podendo ser utilizada como uma ferramenta de linha de comando ou como um servidor. De forma simplificada pode ser entendido como um questionário que é definido através de scripts Lua.
Como funciona uma Pipeline?
Para utilizarmos o sistema, primeiramente devemos criar uma pasta em nosso sistema operacional que será utilizada para armazenar todos os nossos workspaces, cada um contem os seus respectivos scripts Lua. Após isso criamos outra pasta que vai conter o nosso projeto especifico.
O nome da pasta não é restritivo, mas para fins didáticos vou me referir a ela como workspaces.
Exemplo: workspaces/projeto-exemplo
Passo um: criação da tabela de pipelines
A tabela de pipelines é especial e deve ser definida uma única vez no workspace, ela é responsável pela definição de quais são as pipelines presentes no workspace.
Vamos começar criando uma arquivo Lua main.lua dentro de um workspace conforme exemplo acima.
-- Arquivo main.lua
PIPELINES = {
"questionario_dados_pessoais",
"consultar_chamado"
}Acima criamos a tabela PIPELINES que informa ao sistema a existência de duas pipelines: questionario_dados_pessoais e consultar_chamado.
Passo dois: definição de uma pipeline
Vamos agora definir a pipeline questionario_dados_pessoais, para isso devemos criar a tabela própria da pipeline contendo as perguntas que ela possui.
-- Arquivo main.lua
PIPELINES = {
"questionario_dados_pessoais",
"consultar_chamado"
}
questionario_dados_pessoais = {
"perguntar_nome",
"perguntar_idade",
}Perceba que os nomes devem corresponder nas tabelas de PIPELINES e em sua própria definição.
Passo tres: definição das perguntas
Cada pergunta é representada por uma tabela com o seu respectivo nome que foi definido acima, a tabela contem quatro campos:
- output é a resposta da pergunta atual.
- show define se a pergunta deve ser feita ou não
- select é um conjunto de possíveis escolhas mostrada ao usuário junto ao output. O label é a frase mostrada de cada opção, no qual há um número id associado respectivamente.
- accept define se a pergunta atual atende condições especificas e pode ser mostrada.
- reject é a resposta da pergunta atual caso o accept seja falso.
Exemplo completo.
-- Arquivo main.lua
PIPELINES = {
"questionario_dados_pessoais",
"consultar_chamado"
}
questionario_dados_pessoais = {
"perguntar_nome",
"perguntar_idade",
"perguntar_localidade"
}
-- Pergunta simplificada
perguntar_nome = {
output = "Olá, qual é o seu nome?", -- string
show = true, -- boolean
accept = true, -- boolean
reject = "Mensagem de rejeição" -- string
}
-- Pergunta com condições
perguntar_idade = {
output = function(respostas)
-- O output pode ser um função caso deseje utilizar as respostas
-- anteriores
return "Olá" .. respostas.perguntar_nome .. ", qual é a sua idade?"
end,
show = function(respostas)
-- O show pode ser uma função caso deseje fazer alguma condição para
-- determinar se o sistema deve fazer essa pergunta ao usuário ou não
if respostas.perguntar_nome == "Felipe" then
-- se a resposta da primeira pergunta for Felipe
-- o sistema não vai perguntar sua idade
return false
end
return true
end,
accept = function(atual, repostas)
-- Aqui definimos condições para aceitar o input do usuário
-- baseado na resposta atual ou anteriores
if tonumber(atual) < 18 then
return false
end
return true
end,
reject = function(atual, respostas)
-- Podemos usar a reposta atual e as anteriores para rejeitar
-- a resposta do usuário
return "Digite uma idade maior " .. respostas.perguntar_nome
end,
}
perguntar_localidade = {
output = "Localidade:",
show = true,
accept = true,
select = {
{ label = "Acre", id = 2},
{ label = "Alagoas", id = 3 },
{ label = "Amapá", id = 4},
{ label = "Distrito Federal", id = 5},
{ label = "Espírito Santo", id = 6},
{ label = "Goiás", id = 7},
{ label = "Maranhão", id = 8},
{ label = "Mato Grosso", id = 9},
{ label = "Mato Grosso do Sul", id = 10},
{ label = "Minas Gerais", id = 11},
{ label = "Pará", id = 12},
{ label = "Paraíba", id = 13},
{ label = "Paraná", id = 14},
{ label = "Pernambuco", id = 15},
{ label = "Piauí", id = 16},
{ label = "Rio de Janeiro", id = 17},
{ label = "Rio Grande do Norte", id = 18},
{ label = "Rio Grande do Sul", id = 19},
{ label = "Rondônia", id = 20},
{ label = "Roraima", id = 21},
{ label = "Santa Catarina", id = 22},
{ label = "São Paulo", id = 23},
{ label = "Sergipe", id = 24},
{ label = "Tocantins", id = 25},
},
reject = "Mensagem de rejeição"
}Estrutura de projeto e biblioteca global
Dentro da pasta usada para guardar os workspaces, é possível criar um projeto especial com o nome lib_global que compartilha todo o seu conteúdo com os outros projetos e repositórios, ele pode ser usado para escrever funções utilitárias que são usadas amplamente em todos os outros projetos por exemplo.
Estrutura de exemplo:
workspaces
├── cliente-x
│ ├── arquivo_x.lua
│ ├── arquivo_g.lua
│ └── subpasta_x
│ ├── arquivo_t.lua
│ ├── arquivo_y.lua
├── cliente-y
│ ├── arquivo_t.lua
│ └── subpasta_z
│ ├── arquivo_t.lua
│ ├── arquivo_y.lua
└── lib_global
├── arquivo_h.lua
└── subpasta
├── cnbbb.luaaComo pode-se ver a estrutura de pastas dentro de cada projeto é livre para se decidir e usar da forma que preferir.
Bibliotecas disponíveis no Lua
Os seguintes pacotes vão estar disponíveis no ambiente de desenvolvimento através do LuaRocks.
Alem dos pacotes externos está disponível também as seguintes bibliotecas de sistema:
Formas de execução do sistema
O sistema de pipelines possui duas formas de ser executado, ferramenta de linha de comando (CLI) e servidor HTTP.
O sistema pode ser instalado localmente usar o seguinte comando no local do repositório: cargo install --path .
Repl (CLI)
Modo de linha de comando.
Comando para se executar no modo de linha de comando:
cargo run repl --client <CLIENT> --pipeline <PIPELINE>Descrição dos parâmetros
-w, --workdir <WORKDIR> Workspaces directory, uses current dir as default
-c, --client <CLIENT> Client name
-p, --pipeline <PIPELINE> Pipeline name
-h, --help Print help
-V, --version Print versionServer
Modo de servidor HTTP.
Comando para se executar no modo servidor:
cargo run server --port <PORT> --username <USERNAME> --password <PASSWORD>Descrição dos parâmetros
-W, --workdir <WORKDIR> Workspaces directory, uses current dir as default
-S, --sync Synchronize with Git
-P, --port <PORT> Server port
-u, --username Username
-p, --password Password
-h, --help Print help
-V, --version Print versionIMPORTANTE: para se utilizar a sincronização com git é necessário configurar o arquivo Config.toml dentro do seu diretório utilizado como workdir utilizando o seguinte modelo:
Exemplo de arquivo Config.toml
[cliente-xpto]
url = "https://github.com/centralit-governanca-corporativa/cliente-xpto.git"
username = "git-user"
password = "secret-password"
[nome-do-cliente]
url = "url-do-github"
username = "git-user"
password = "secret-password"Documentação da API
A API suporta requisições para quatro recursos diferentes:
Workspaces
Recursos utilizados para manipular os workspaces.
Reload All Workspaces
- Method: GET
- Endpoint: /workspace/reload
- Description: Reloads all workspaces
Reload One Workspace
- Method: GET
- Endpoint: /workspace/reload/{client}
- Description: Reloads a specific workspace identified by the client.
Git
Recursos utilizados para se sincronizar o GIT.
Sync All Repositories
- Method: GET
- Endpoint: /git/sync
- Description: Synchronizes all Git repositories.
Sync One Repository
- Method: GET
- Endpoint: /git/sync/{client}
- Description: Synchronizes a specific Git repository identified by the client.
Conversation
Recursos utilizadas para interagir com as conversas.
IMPORTANTE: Esse recurso está protegido por Basic auth, as credenciais são as mesmas utilizadas na inicialização do servidor.
Create Conversation
- Method: POST
- Endpoint: /conversation/spawn/{client}/{pipeline}/{conversation_uuid}
- Authentication: Basic auth
- Description: Creates a new conversation instance within a specific pipeline and conversation_uuid for the given client.
Process Conversation
- Method: POST
- Endpoint: /conversation/process/{conversation_uuid}
- Authentication: Basic auth
- Description: Processes the specified conversation within the given pipeline for the client.
Body:
{ input: "User input here" }Has Conversation
- Method: GET
- Endpoint: /conversation/{conversation_uuid}
- Authentication: Basic auth
- Description: Verify if a conversation within a specific conversation_uuid exists.
Exposição de funções para Web
É possível expor funções que podem ser chamadas através de endpoints específicos que serão consumidos por outros serviços que desejem se comunicar com nosso sistema pelos protocolos http.
Para expor alguma função primeiro é necessário declarar uma tabela. Essa tabela pode ser declarada dentro de um arquivo que já contenha um fluxo conversacional das pipelines ou pode ser criado em um arquivo separado dentro da pasta do projeto.
Para nosso exemplo, criaremos um arquivo chamado webservice workspaces/projeto-exemplo/webservice.lua
Dentro do arquivo temos 5 (cinco) tabelas.
-- Arquivo webservice.lua
ALLOW_GET_ROUTES = {}
ALLOW_POST_ROUTES = {}
ALLOW_PUT_ROUTES = {}
ALLOW_PATCH_ROUTES = {}
ALLOW_DELETE_ROUTES = {}Cada tabela corresponde a um método HTTP (GET, POST, PUT, PATCH e DELETE).
Agora vamos criar uma função dentro desse arquivo.
-- Arquivo webservice.lua
function teste()
return "Hello Word"
end
Com a função criada, precisamos expô-la para que seja acessada via web. Neste caso vamos expô-la para que seja acessada pelo método GET.
-- Arquivo webservice.lua
ALLOW_GET_ROUTES = {
"teste"
}
ALLOW_POST_ROUTES = {}
ALLOW_PUT_ROUTES = {}
ALLOW_PATCH_ROUTES = {}
ALLOW_DELETE_ROUTES = {}
function teste()
return "Hello Word"
endDessa forma, podemos acessar essa função construindo a URL abaixo:
http://dominio-exemplo/get/projeto-exemplo/teste
- dominio-exemplo: domínio que roda o sistema
- get: constitui a url para informar que iremos buscar uma função declarada no ALLOW_GET_ROUTES -- O método http também deve ser o GET
- projeto-exemplo: Nome do projeto em que o arquivo webservice.lua se encontra
- teste: nome da função que você deseja acessar.
Chamado então esse endpoint, você receberá como reposta o que foi retornado na função Hello Word.
IMPORTANTE
Todas as funções recebem 3 (três) parâmetros. O primeiro são os headers da requisição, o segundo os query params e o terceiro o body.
Dessa forma podemos modificar nossa função afim de receber esses parametros e realizar alguma lógica utilizando-os.
-- Arquivo webservice.lua
ALLOW_GET_ROUTES = {
"teste"
}
ALLOW_POST_ROUTES = {}
ALLOW_PUT_ROUTES = {}
ALLOW_PATCH_ROUTES = {}
ALLOW_DELETE_ROUTES = {}
function teste(headers, query)
--lógica a ser aplicada com os parâmetros
return "Hello Word"
endOBSERVAÇÃO 1: Caso você use o parâmetro query e no endpoint não seja enviado query params, ela estará vázio.
OBSERVAÇÃO 2: Caso você declare um terceiro parâmetro na função ele será entendido como o body da requisição, e no caso do método HTTP GET, que não há body, ele virá como uma string vazia.
Vamos agora criar outra função para ser chamada via método HTTP POST.
Criaremos a função consulta_autorizacao no mesmo arquivo que estávamos webservice.lua. Essa função receberá os 3 (três) parâmetros citados acima. Ela será exposta na tabela ALLOW_POST_ROUTES para estar disponível para acesso via HTTP.
-- Arquivo webservice.lua
ALLOW_GET_ROUTES = {
"teste"
}
ALLOW_POST_ROUTES = {
"consulta_autorizacao"
}
ALLOW_PUT_ROUTES = {}
ALLOW_PATCH_ROUTES = {}
ALLOW_DELETE_ROUTES = {}
function teste()
return "Hello Word"
end
function consulta_autorizacao(headers, query, body)
--lógica para consultar alguma autorização
return "Autorizado"
endO endpoint para chamar a função que criamos agora é http://dominio-exemplo/post/projeto-exemplo/consulta_autorizacao. Lembrando que essa função foi exposta no ALLOW_POST_ROUTES, sendo assim é necessário que você utilize o método HTTP POST e na construção da url, após o dominio-base, você deverá insirir /post/ para que sua função seja chamada corretamente.