@indra.ai/deva.data
Version:
The Data Deva manages data ensuring in the Deva.world ecosystem.
665 lines (616 loc) • 20.7 kB
JavaScript
// Copyright (c)2023 Quinn Michaels
// Data Deva
import Deva from '@indra.ai/deva';
import { MongoClient, ObjectId } from 'mongodb';
import pkg from './package.json' with {type:'json'};
const {agent, vars} = pkg.data;
// set the __dirname
import {dirname} from 'node:path';
import {fileURLToPath} from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const info = {
id: pkg.id,
name: pkg.name,
version: pkg.version,
author: pkg.author,
describe: pkg.description,
dir: __dirname,
url: pkg.homepage,
git: pkg.repository.url,
bugs: pkg.bugs.url,
license: pkg.license,
copyright: pkg.copyright
};
const DATA = new Deva({
info,
agent,
vars,
utils: {
translate(input) {return input.trim();},
parse(input) {return input.trim();},
process(input) {return input.trim();},
memory(input) {
return input.replace(/\n/g, ' ')
.replace(/(\b)or have specific questions about it(\b)/g, '$2')
.replace(/(\b), feel free to ask(\b)/g, '$2')
.replace(/\sIf you have .+ free share!/g, '')
.replace(/\sIf there are .+ free share!/g, '')
.replace(/\s{2,}/g, ' ');
}
},
listeners: {
'data:history'(packet) {
this.context('history');
// here we insert a history object into the database.
this.func.insert({
collection: 'history',
data: packet,
});
},
async 'data:memory'(packet) {
const datamem = await this.func.insert({
collection: `memory_${packet.agent.key}`,
data: {
id: packet.id,
client: {
id: packet.client.id,
name: packet.client.profile.name,
},
agent: {
id: packet.agent.id,
key: packet.agent.key,
name: packet.agent.profile.name,
},
q: this.utils.memory(packet.q),
a: this.utils.memory(packet.a),
created: Date.now(),
}
});
}
},
modules: {
client: false,
},
devas: {},
func: {
/**************
func: insert
params: opts
describe: the insert function that inserts into the specified collection.
***************/
async insert(opts) {
this.action('func', `insert ${opts.collection}`);
let result = false;
try {
this.state('data', `insert ${opts.collection}`);
await this.modules.client.connect(); // connect to the database client.
const db = this.modules.client.db(this.vars.database); // set the database to use
result = await db.collection(opts.collection).insertOne(opts.data); // insert the data
} finally {
await this.modules.client.close(); // close the connection when done
this.action('return', `insert ${opts.collection}`);
return result; // return the result to the requestor.
}
},
/**************
func: update
params: opts
describe: the update function that update into the specified collection.
***************/
async update(opts) {
this.action('func', 'update');
let result = false;
try {
this.state('update', opts.collection);
await this.modules.client.connect(); // connect to the database client.
const db = this.modules.client.db(this.vars.database); // set the database to use
result = await db.collection(opts.collection).updateOne(
{ _id: new ObjectId(`${opts.id}`) },
{ $set: opts.data }
); // insert the data
} finally {
await this.modules.client.close(); // close the connection when done
this.state('return', 'update');
return result; // return the result to the requestor.
}
},
/**************
func: list
params: obj - the find object
describe: return a find from the database collection.
***************/
async list(obj={}) {
this.action('func', 'list');
let result = false;
const {collection,data} = obj;
try {
await this.modules.client.connect();
const db = this.modules.client.db(this.vars.database);
result = await db.collection(collection).find(data).sort({created:1}).toArray();
} finally {
await this.modules.client.close();
return result;
}
},
/**************
func: search
params: obj - the search object
describe: return a search from the database collection.
***************/
async search(opts) {
this.action('func', 'search');
let result = false;
const {collection,limit} = this.vars.search;
try {
this.state('search', opts.text);
await this.modules.client.connect();
const db = this.modules.client.db(this.vars.database);
const table = db.collection(collection);
// await table.dropIndex('a.text_1');
// const newIndex = await table.createIndex({"a.text": "text"});
const idx = await table.listIndexes().toArray();
// Print the list of indexes
// console.log("Existing indexes:\n", idx);
const query = {$text:{$search:opts.text}};
const projection = {
_id:0,
a: {
id: 1,
text: 1
},
score: { $meta: "textScore" }
};
result = await table.find(query).project(projection).limit(limit).toArray();
} finally {
await this.modules.client.close();
this.state('return', 'search');
return result;
}
},
/**************
func: memory
params: obj - the memory object
describe: return a search from the memory collection.
***************/
async memory(opts) {
this.action('func', 'memory');
let result = false;
const {collection,limit} = this.vars.memory;
try {
this.state('get', `memory`);
await this.modules.client.connect();
const db = this.modules.client.db(this.vars.database);
const table = db.collection(collection);
// await table.dropIndex('a_text_q_text');
const idx = await table.listIndexes().toArray();
const hasIdx = idx.find(i => i.name === 'a_q_text')
if (!hasIdx) {
const newIdx = await table.createIndex({"a": "text", "q": "text"}, {name: 'a_q_text'});
}
const query = {$text:{$search:opts.text}};
const options = {
projection: {
id: 1,
a: 1,
q: 1,
score: { $meta: "textScore" },
created: 1
}
};
// db.memory_buddy.find({$text:{$search:"hello"}}, {id: 1,a: 1,q: 1,score:{$meta:"textScore"}}).limit(10)
result = await table.find(query, options).limit(parseInt(limit)).toArray();
} finally {
await this.modules.client.close();
this.state('return', `memory`);
return result;
}
},
/**************
func: knowledge
params: obj - the knowledge object
describe: return a search from the knowledge collection.
***************/
async knowledge(opts) {
this.action('func', 'knowledge');
let result = false;
const {collection,limit} = this.vars.knowledge;
try {
this.state('get', 'knowledge');
await this.modules.client.connect();
const db = this.modules.client.db(this.vars.database);
const table = db.collection(collection);
// await table.dropIndex('a_text_q_text');
const idx = await table.listIndexes().toArray();
const hasIdx = idx.find(i => i.name === 'knowledge_text')
if (!hasIdx) {
const newIdx = await table.createIndex({"content": "text"}, {name: 'knowledge_text'});
}
const query = {$text:{$search:opts.text}};
const options = {
projection: {
id: 1,
content: 1,
score: { $meta: "textScore" }
}
};
result = await table.find(query,options).limit(parseInt(limit)).toArray();
} finally {
await this.modules.client.close();
this.state('return', `knowledge`);
return result;
}
},
/**************
func: index
params: opts
describe: Creates an index on a collection in the database.
***************/
async indexes(opts) {
this.action('func', 'indexes');
let result = false;
// get indexes
try {
this.state('get', `indexes`);
await this.modules.client.connect();
const db = this.modules.client.db(this.vars.database);
result = await db.collection(opts.collection).listIndexes().toArray();
} finally {
await this.modules.client.close();
this.state('return', 'indexes');
return result;
}
},
/**************
func: history
params: opts
describe: return history
***************/
async history() {
this.action('func', 'history');
let result = false;
const {collection,limit} = this.vars.history;
try {
this.state('get', `history`);
await this.modules.client.connect();
const db = this.modules.client.db(this.vars.database);
result = await db.collection(collection).find({}).sort({created:-1}).limit(limit).toArray();
} finally {
await this.modules.client.close();
this.state('return', `history`);
return result;
}
},
},
methods: {
/**************
method: insert
params: packet
describe: insert data into the data vault.
***************/
insert(packet) {
this.context('insert', `collection:${packet.q.meta.params[1]}`);
this.action('method', `insert:${packet.q.meta.params[1]}`);
return new Promise((resolve, reject) => {
const {data, meta} = packet.q;
const collection = meta.params[1];
this.func.insert({collection,data}).then(ins => {
this.state('resolve', `insert:${collection}:${insinsertedId}`)
return resolve({
text: `id:${insinsertedId}`,
html: `id:${insinsertedId}`,
data: ins,
});
}).catch(err => {
this.state('reject', 'insert');
return this.error(packet, err, reject);
});
});
},
/**************
method: model
params: packet
params[1] is the agent
params[2] is the group
params[3] is the role
params[4] is the id (for edits)
describe: model method for building the data model.
***************/
model(packet) {
this.context('model');
return new Promise((resolve, reject) => {
const {meta, text} = packet.q;
let func = 'insert', id = false;
if (meta.params[1]) this.vars.model.agent = meta.params[1];
if (meta.params[2]) this.vars.model.group = meta.params[2];
if (meta.params[3]) this.vars.model.role = meta.params[3];
const { collection } = this.vars.model;
const data = {
agent: this.vars.model.agent,
group: this.vars.model.group,
role: this.vars.model.role,
content: text,
};
if (meta.params[4]) {
id = meta.params[4];
func = 'update';
data.modified = Date.now();
}
else {
data.modified = null;
data.created = Date.now();
}
this.func[func]({id, collection,data}).then(ins => {
return resolve({
text: `id:${ins.insertedId || id}`,
html: `id:${ins.insertedId || id}`,
data: ins,
});
}).catch(err => {
return this.error(packet, err, reject);
});
});
},
/**************
method: modeler
params: packet
params[1] is the agent
params[2] is the group
params[3] is the role
describe: model method for building the data model.
***************/
modeler(packet) {
this.context('modeler');
return new Promise((resolve, reject) => {
const {meta, text} = packet.q;
if (meta.params[1]) this.vars.modeler.agent = meta.params[1];
const { collection, agent } = this.vars.modeler;
const data = {agent};
this.func.list({collection,data}).then(list => {
const model = {};
const data = [];
// loop of the array object
for (const x of list) {
if (!model[x.group]) model[x.group] = [];
model[x.group].push({role: x.role, content: x.content});
}
// loop in the data object.
for (const x in model) {
data.push(JSON.stringify({messages: model[x]}));
}
// format for jsonl
return resolve({
text: `see data`,
html: `see data`,
data,
});
}).catch(err => {
return this.error(packet, err, reject);
});
});
},
/**************
method: history
params: packet
describe: get history
***************/
history(packet) {
this.context('history');
return new Promise((resolve, reject) => {
this.func.history().then(history => {
return resolve({
text: 'see data',
html: 'see data',
data: history,
})
}).catch(err => {
return this.error(packet, err, reject);
});
});
},
/**************
method: history
params: packet
describe: get history
***************/
search(packet) {
this.context('search', packet.q.text);
this.action('method', `search:${packet.q.text}`);
return new Promise((resolve, reject) => {
if (!packet.q.text) return resolve(this._messages.notext);
const {params} = packet.q.meta;
if (params[1]) this.vars.search.collection = packet.q.meta.params[1];
if (params[2]) this.vars.search.limit = packet.q.meta.params[2];
this.func.search(packet.q).then(search => {
this.state('resolve', `search:${packet.q.text}`);
return resolve({
text: 'see data',
html: 'see data',
data: search,
})
}).catch(err => {
return this.error(packet, err, reject);
});
});
},
/**************
method: memory
params: packet
describe: search memory
***************/
memory(packet) {
this.context('memory', packet.q.text);
this.action('method', `memory`);
return new Promise((resolve, reject) => {
if (!packet.q.text) return resolve(this._messages.notext);
const {params} = packet.q.meta;
const data = {};
if (params[1]) this.vars.memory.collection = `memory_${params[1]}`;
if (params[2]) this.vars.memory.limit = packet.q.meta.params[2];
this.func.memory(packet.q).then(memory => {
data.memory = memory;
const text = memory ? memory.map(mem => {
return [
`::begin:memory:${mem.id}`,
`question: ${mem.q}`,
`answer: ${mem.a}`,
`date: ${this.formatDate(mem.created, 'long', true)}`,
`score: ${mem.score.toFixed(3)}`,
`::end:memory:${this.hash(mem)}`,
].join('\n');
}).join('\n') : 'no memory';
this.state('parse', `memory`);
console.log('packet memory feecting resolve', memory);
return this.question(`${this.askChr}feecting parse ${text}`);
}).then(feecting => {
data.feecting = feecting.a.data;
this.state('resolve', `memory`);
return resolve({
text: feecting.a.text,
html: feecting.a.html,
data,
})
}).catch(err => {
this.state('reject', `memory`)
return this.error(packet, err, reject);
});
});
},
/**************
method: knowledge
params: packet
describe: Knowledge base wisdom
***************/
knowledge(packet) {
this.context('knowledge', packet.q.text);
this.action('method', `knowledge`);
return new Promise((resolve, reject) => {
if (!packet.q.text) return resolve(this._messages.notext);
const {params} = packet.q.meta;
const data = {};
if (params[1]) this.vars.knowledge.limit = params[1];
this.func.knowledge(packet.q).then(wisdom => {
data.wisdom = wisdom;
const text = wisdom ? wisdom.map(item => {
return [
`::begin:knowledge:${item.id}`,
`law: ${item.content}`,
`created: ${this.formatDate(item.created, 'long', true)}`,
`score: ${item.score.toFixed(3)}`,
`::end:knowledge:${this.hash(item)}`,
].join('\n');
}).join('\n') : 'no knowledge';
this.state('parse', `knowledge`);
return this.question(`${this.askChr}feecting parse ${text}`);
}).then(feecting => {
data.feecting = feecting.a.data;
this.state('resolve', `knowledge`);
return resolve({
text: feecting.a.text,
html: feecting.a.html,
data,
})
}).catch(err => {
this.state('reject', `knowledge`)
return this.error(err, packet, reject);
});
});
},
/**************
method: mem
params: packet
describe: add data to the knowledge base
example: #data mem:[id] [content to store in memory]
***************/
add(packet) {
this.context('add', `text: ${packet.q.meta.params[1]}`);
return new Promise((resolve, reject) => {
if (!packet.q.text) return resolve(this._messages.notext);
this.vars.knowledge.content = packet.q.text; // store text in local
const {meta, text} = packet.q;
let func = 'insert', id = false;
const {collection, content} = this.vars.knowledge;
const data = {content};
// if param[1] id is found then update record
if (meta.params[1]) {
id = meta.params[1];
func = 'update';
data.modified = Date.now();
}
else {
data.modified = null;
data.created = Date.now();
}
this.func[func]({id,collection,data}).then(ins => {
this.state('resolve', 'add');
return resolve({
text: `id: ${ins.insertedId || id}`,
html: `id: ${ins.insertedId || id}`,
data: ins,
});
}).catch(err => {
this.state('reject', 'add');
return this.error(err, packet, reject);
});
});
},
/**************
method: listidx
params: packet
describe: List Indexes
***************/
listidx(packet) {
this.context('index', packet.q.text);
this.action('method', 'index');
return new Promise((resolve, reject) => {
this.func.indexes(packet.q).then(idx => {
this.state('resolve', 'indexes');
return resolve({
text: 'indexes',
html: 'indexes',
data: idx
})
}).reject(err => {
return this.error(err, packet, reject);
})
});
},
extract(packet) {
return new Promise((resolve, reject) => {
try {
const theFile = this.lib.fs.readFileSync(`./private/data/extract/conversations.json`);
const theJSON = JSON.parse(theFile);
const theConvo = [];
theJSON.forEach((itm,idx) => {
const getToday = this.lib.getToday(itm.create_time * 1000);
const writeFile = `./private/data/conversations/${getToday}.json`
const writeData = JSON.stringify(itm, false, 2);
this.lib.fs.writeFileSync(writeFile, writeData);
this.prompt(`extract file:${writeFile}`);
// console.log('item', itm);
});
return resolve({
text: Object.keys(theJSON),
html: false,
data: Object.keys(theJSON),
});
}
catch (err) {
return this.error(err, packet, reject);
}
});
}
},
onReady(data, resolve) {
const {uri,database} = this.services().personal.mongo;
this.modules.client = new MongoClient(uri);
this.vars.database = database;
this.prompt(this.vars.messages.ready);
return resolve(data);
},
onError(err, data, reject) {
this.prompt(this.vars.messages.error);
console.log(err);
return reject ? reject(err) : false;
},
});
export default DATA