new(all): Auto import for logo, colors and website content.
This commit is contained in:
parent
7fe50d95c4
commit
e91c3a4e06
7 changed files with 176 additions and 65 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -28,3 +28,5 @@ yarn-error.log
|
|||
package-lock.json
|
||||
yarn-lock.json
|
||||
packages/saas.gbapp.zip
|
||||
logo.svg
|
||||
screenshot.png
|
||||
|
|
|
@ -59,9 +59,11 @@ import { GBHubSpotPackage } from '../../hubspot.gblib/index.js';
|
|||
import open from 'open';
|
||||
import ngrok from 'ngrok';
|
||||
import Path from 'path';
|
||||
import { file } from 'googleapis/build/src/apis/file/index.js';
|
||||
import { GBUtil } from '../../../src/util.js';
|
||||
import { GBLogEx } from './GBLogEx.js';
|
||||
import { GBDeployer } from './GBDeployer.js';
|
||||
import { SystemKeywords } from '../../basic.gblib/services/SystemKeywords.js';
|
||||
import { DialogKeywords } from '../../basic.gblib/services/DialogKeywords.js';
|
||||
|
||||
/**
|
||||
* GBCoreService contains main logic for handling storage services related
|
||||
|
@ -665,6 +667,50 @@ ENDPOINT_UPDATE=true
|
|||
await installationDeployer.openStorageFirewall(group, serverName);
|
||||
}
|
||||
|
||||
public async setConfig(min, name: string, value: any): Promise<any> {
|
||||
|
||||
// Handles calls for BASIC persistence on sheet files.
|
||||
|
||||
GBLog.info( `Defining Config.xlsx variable ${name}= '${value}'...`);
|
||||
|
||||
let { baseUrl, client } = await GBDeployer.internalGetDriveClient(min);
|
||||
|
||||
const maxLines = 512;
|
||||
const file = "Config.xlsx";
|
||||
const path = DialogKeywords.getGBAIPath(min.botId, `gbot`);;
|
||||
|
||||
let document = await (new SystemKeywords()).internalGetDocument(client, baseUrl, path, file);
|
||||
|
||||
// Creates workbook session that will be discarded.
|
||||
|
||||
let sheets = await client
|
||||
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets`)
|
||||
.get();
|
||||
|
||||
let results = await client
|
||||
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='A1:A${maxLines}')`)
|
||||
.get();
|
||||
|
||||
const rows = results.text;
|
||||
let address = '';
|
||||
|
||||
// Fills the row variable.
|
||||
|
||||
for (let i = 1; i <= rows.length; i++) {
|
||||
let result = rows[i - 1][0];
|
||||
if (result && result.toLowerCase() === name.toLowerCase()) {
|
||||
address = `B${i}:B${i}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let body = { values: [[]] };
|
||||
body.values[0][0] = value;
|
||||
|
||||
await client
|
||||
.api(`${baseUrl}/drive/items/${document.id}/workbook/worksheets('${sheets.value[0].name}')/range(address='${address}')`)
|
||||
.patch(body);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -687,7 +687,13 @@ export class GBMinService {
|
|||
paramLogoImageAlt: this.core.getParam(instance, 'Logo Image Alt', null),
|
||||
paramLogoImageWidth: this.core.getParam(instance, 'Logo Image Width', null),
|
||||
paramLogoImageHeight: this.core.getParam(instance, 'Logo Image Height', null),
|
||||
paramLogoImageType: this.core.getParam(instance, 'Logo Image Type', null)
|
||||
paramLogoImageType: this.core.getParam(instance, 'Logo Image Type', null),
|
||||
logo: this.core.getParam(instance, 'Logo', null),
|
||||
color1: this.core.getParam(instance, 'Color1', null),
|
||||
color2: this.core.getParam(instance, 'Color2', null),
|
||||
|
||||
|
||||
|
||||
})
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -39,7 +39,7 @@ import GBBulletPlayer from './players/GBBulletPlayer.js';
|
|||
import SidebarMenu from './components/SidebarMenu.js';
|
||||
import SEO from './components/SEO.js';
|
||||
import GBCss from './components/GBCss.js';
|
||||
import { DirectLine} from 'botframework-directlinejs';
|
||||
import { DirectLine } from 'botframework-directlinejs';
|
||||
import { ConnectionStatus } from 'botframework-directlinejs';
|
||||
import ReactWebChat from 'botframework-webchat';
|
||||
import { UserAgentApplication } from 'msal';
|
||||
|
@ -257,16 +257,16 @@ class GBUIApp extends React.Component {
|
|||
);
|
||||
break;
|
||||
case 'multiurl':
|
||||
playerComponent = (
|
||||
<GBMultiUrlPlayer
|
||||
app={this}
|
||||
ref={player => {
|
||||
this.player = player;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case 'image':
|
||||
playerComponent = (
|
||||
<GBMultiUrlPlayer
|
||||
app={this}
|
||||
ref={player => {
|
||||
this.player = player;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case 'image':
|
||||
playerComponent = (
|
||||
<GBImagePlayer
|
||||
app={this}
|
||||
|
@ -305,18 +305,17 @@ class GBUIApp extends React.Component {
|
|||
let chat = <div />;
|
||||
let gbCss = <div />;
|
||||
let seo = <div />;
|
||||
|
||||
let sideBar = (
|
||||
<div className="sidebar">
|
||||
<SidebarMenu chat={this.chat} instance={this.state.instanceClient} />
|
||||
</div>
|
||||
);
|
||||
let sideBar = <div />;
|
||||
|
||||
if (this.state.line) {
|
||||
if (this.state.instanceClient) {
|
||||
let color1 = this.state.instanceClient.color1;
|
||||
gbCss = <GBCss instance={this.state.instanceClient} />;
|
||||
seo = <SEO instance={this.state.instanceClient} />;
|
||||
const token = this.state.instanceClient.speechToken;
|
||||
|
||||
document.body.style.setProperty('background-color', this.state.instanceClient.color2, 'important');
|
||||
|
||||
chat = (
|
||||
<ReactWebChat
|
||||
ref={chat => {
|
||||
|
@ -329,15 +328,25 @@ class GBUIApp extends React.Component {
|
|||
})}
|
||||
/>
|
||||
);
|
||||
|
||||
sideBar = (
|
||||
<div
|
||||
className="sidebar"
|
||||
ref={node => {
|
||||
if (node) {
|
||||
node.style.setProperty('background-color', this.state.instanceClient.color1, 'important');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SidebarMenu chat={this.chat} instance={this.state.instanceClient} />
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.state.instanceClient) {
|
||||
sideBar = '';
|
||||
}
|
||||
|
||||
return (
|
||||
<StaticContent>
|
||||
|
||||
{seo}
|
||||
<div>
|
||||
{gbCss}
|
||||
|
|
|
@ -49,7 +49,7 @@ class SideBarMenu extends React.Component {
|
|||
<div className="tittleSideBarMenu">
|
||||
<img
|
||||
className="pragmatismoLogo"
|
||||
src={"/themes/" + this.props.instance.theme + "/images/logo.png"}
|
||||
src={this.props.instance.botId + "/cache/" + this.props.instance.logo}
|
||||
alt="General Bots Logo" />
|
||||
|
||||
</div>
|
||||
|
|
|
@ -254,29 +254,28 @@ export class ChatServices {
|
|||
public static async answerByGPT(
|
||||
min: GBMinInstance,
|
||||
user,
|
||||
pid,
|
||||
question: string,
|
||||
searchScore: number,
|
||||
subjects: GuaribasSubject[]
|
||||
question: string, mode=null
|
||||
) {
|
||||
if (!process.env.OPENAI_API_KEY) {
|
||||
return { answer: undefined, questionId: 0 };
|
||||
}
|
||||
|
||||
const LLMMode = min.core.getParam(min.instance, 'Answer Mode', 'direct');
|
||||
const LLMMode = mode??min.core.getParam(min.instance, 'Answer Mode', 'direct');
|
||||
|
||||
const docsContext = min['vectorStore'];
|
||||
|
||||
if (!this.memoryMap[user.userSystemId]) {
|
||||
this.memoryMap[user.userSystemId] = new BufferWindowMemory({
|
||||
returnMessages: true,
|
||||
memoryKey: 'chat_history',
|
||||
inputKey: 'input',
|
||||
k: 2
|
||||
});
|
||||
const memory = new BufferWindowMemory({
|
||||
returnMessages: true,
|
||||
memoryKey: 'chat_history',
|
||||
inputKey: 'input',
|
||||
k: 2
|
||||
});
|
||||
|
||||
if (user && !this.memoryMap[user.userSystemId]) {
|
||||
this.memoryMap[user.userSystemId] = memory;
|
||||
}
|
||||
const memory = this.memoryMap[user.userSystemId];
|
||||
const systemPrompt = this.userSystemPrompt[user.userSystemId];
|
||||
|
||||
const systemPrompt = user?this.userSystemPrompt[user.userSystemId]:'';
|
||||
|
||||
const model = new ChatOpenAI({
|
||||
openAIApiKey: process.env.OPENAI_API_KEY,
|
||||
|
|
|
@ -53,7 +53,6 @@ import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
|
|||
import { Document } from 'langchain/document';
|
||||
import getColors from 'get-image-colors';
|
||||
|
||||
|
||||
import {
|
||||
GBDialogStep,
|
||||
GBLog,
|
||||
|
@ -377,7 +376,7 @@ export class KBService implements IGBKBService {
|
|||
returnedScore: ${returnedScore} < required (searchScore): ${searchScore}`
|
||||
);
|
||||
|
||||
return await ChatServices.answerByGPT(min, user, pid, query, searchScore, subjects);
|
||||
return await ChatServices.answerByGPT(min, user, query);
|
||||
}
|
||||
|
||||
public async getSubjectItems(instanceId: number, parentId: number): Promise<GuaribasSubject[]> {
|
||||
|
@ -853,13 +852,13 @@ export class KBService implements IGBKBService {
|
|||
}
|
||||
|
||||
async saveHtmlPage(min, url: string, page: Page): Promise<string | null> {
|
||||
page.setCacheEnabled(false);
|
||||
const response = await page.goto(url);
|
||||
|
||||
|
||||
if (response.headers && response.status() === 200) {
|
||||
if (response.headers && (response.status() === 200 || response.status() === 304)) {
|
||||
const contentType = response.headers()['content-type'];
|
||||
if (contentType && contentType.includes('text/html')) {
|
||||
const buffer = await response.buffer();
|
||||
const buffer = await page.content();
|
||||
const urlObj = new URL(url);
|
||||
const urlPath = urlObj.pathname.endsWith('/') ? urlObj.pathname.slice(0, -1) : urlObj.pathname; // Remove trailing slash if present
|
||||
let filename = urlPath.split('/').pop() || 'index'; // Get the filename from the URL path or set it to 'index.html' as default
|
||||
|
@ -882,12 +881,12 @@ export class KBService implements IGBKBService {
|
|||
try {
|
||||
if (
|
||||
depth > maxDepth ||
|
||||
(visited.has(url) ||
|
||||
url.endsWith('.jpg') ||
|
||||
url.endsWith('.pdf') ||
|
||||
url.endsWith('.jpg') ||
|
||||
url.endsWith('.png') ||
|
||||
url.endsWith('.mp4'))
|
||||
visited.has(url) ||
|
||||
url.endsWith('.jpg') ||
|
||||
url.endsWith('.pdf') ||
|
||||
url.endsWith('.jpg') ||
|
||||
url.endsWith('.png') ||
|
||||
url.endsWith('.mp4')
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
@ -904,6 +903,7 @@ export class KBService implements IGBKBService {
|
|||
}
|
||||
const currentDomain = new URL(page.url()).hostname;
|
||||
let links = await page.evaluate(currentDomain => {
|
||||
|
||||
const anchors = Array.from(document.querySelectorAll('a')).filter(p => {
|
||||
try {
|
||||
return currentDomain == new URL(p.href).hostname;
|
||||
|
@ -938,7 +938,7 @@ export class KBService implements IGBKBService {
|
|||
const childLinks = [];
|
||||
for (const link of filteredLinks) {
|
||||
const links = await this.crawl(min, link, visited, depth + 1, maxDepth, page);
|
||||
if (links){
|
||||
if (links) {
|
||||
childLinks.push(...links);
|
||||
}
|
||||
}
|
||||
|
@ -946,9 +946,41 @@ export class KBService implements IGBKBService {
|
|||
return [filename, ...childLinks]; // Include the filename of the cached file
|
||||
} catch (error) {
|
||||
await GBLogEx.info(min, error);
|
||||
return []; // Include the filename of the cached file
|
||||
}
|
||||
}
|
||||
|
||||
async getLogoByPage(page) {
|
||||
const checkPossibilities = async (page, possibilities) => {
|
||||
for (const possibility of possibilities) {
|
||||
const { tag, attributes } = possibility;
|
||||
|
||||
for (const attribute of attributes) {
|
||||
const selector = `${tag}[${attribute}*="logo"]`;
|
||||
const elements = await page.$$(selector);
|
||||
|
||||
for (const element of elements) {
|
||||
const src = await page.evaluate(el => el.getAttribute('src'), element);
|
||||
if (src) {
|
||||
return src;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// Array of possibilities to check for the logo
|
||||
const possibilities = [
|
||||
{ tag: 'img', attributes: ['src', 'alt', 'class'] }, // Check for img elements with specific attributes
|
||||
{ tag: 'svg', attributes: ['class', 'aria-label'] } // Check for svg elements with specific attributes
|
||||
// Add more possibilities as needed
|
||||
];
|
||||
|
||||
return await checkPossibilities(page, possibilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import all .docx files in reading comprehension folder.
|
||||
*/
|
||||
|
@ -960,32 +992,50 @@ export class KBService implements IGBKBService {
|
|||
): Promise<any> {
|
||||
let files = [];
|
||||
|
||||
Fs.rmSync(min['vectorStorePath'], { recursive: true, force: true });
|
||||
let path = DialogKeywords.getGBAIPath(min.botId, `gbot`);
|
||||
const directoryPath = Path.join(process.env.PWD, 'work', path, 'Website');
|
||||
Fs.rmSync(directoryPath, { recursive: true, force: true });
|
||||
|
||||
const website = min.core.getParam<string>(min.instance, 'Website', null);
|
||||
|
||||
if (website) {
|
||||
const browser = await puppeteer.launch({ headless: false });
|
||||
const page = await browser.newPage();
|
||||
const response = await page.goto(website);
|
||||
await page.setRequestInterception(true);
|
||||
|
||||
await page.screenshot({ path: 'screenshot.png' });
|
||||
page.on('request', req => {
|
||||
if (req.resourceType() === 'image' || req.resourceType() === 'stylesheet') {
|
||||
req.abort();
|
||||
} else {
|
||||
req.continue();
|
||||
}
|
||||
});
|
||||
await page.goto(website);
|
||||
|
||||
const logo = await this.getLogoByPage(page);
|
||||
let path = DialogKeywords.getGBAIPath(min.botId);
|
||||
const logoPath = Path.join(process.env.PWD, 'work', path, 'cache');
|
||||
const baseUrl = page.url().split('/').slice(0, 3).join('/');
|
||||
const logoBinary = await page.goto(urlJoin(baseUrl, logo));
|
||||
const buffer = await logoBinary.buffer();
|
||||
const logoFilename = Path.basename(logo);
|
||||
Fs.writeFileSync(Path.join(logoPath, logoFilename), buffer);
|
||||
await min.core['setConfig'](min, 'Logo', logoFilename);
|
||||
|
||||
// Extract dominant colors from the screenshot
|
||||
|
||||
await page.screenshot({ path: 'screenshot.png' });
|
||||
const colors = await getColors('screenshot.png');
|
||||
await min.core['setConfig'](min, 'Color1', colors[0].hex());
|
||||
await min.core['setConfig'](min, 'Color2', colors[1].hex());
|
||||
|
||||
// Assuming you want the two most dominant colors
|
||||
const mainColor1 = colors[0].hex();
|
||||
const mainColor2 = colors[1].hex();
|
||||
|
||||
console.log('Main Color 1:', mainColor1);
|
||||
console.log('Main Color 2:', mainColor2);
|
||||
|
||||
|
||||
const maxDepth = 1; // Maximum depth of recursion
|
||||
const maxDepth = 2; // Maximum depth of recursion
|
||||
const visited = new Set<string>();
|
||||
files = files.concat(await this.crawl(min, website, visited, 0, maxDepth, page));
|
||||
|
||||
|
||||
await browser.close();
|
||||
|
||||
|
||||
files.shift();
|
||||
|
||||
await CollectionUtil.asyncForEach(files, async file => {
|
||||
|
@ -997,7 +1047,6 @@ export class KBService implements IGBKBService {
|
|||
await vectorStore.addDocuments(flattenedDocuments);
|
||||
await vectorStore.save(min['vectorStorePath']);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
files = await walkPromise(urlJoin(localPath, 'docs'));
|
||||
|
|
Loading…
Add table
Reference in a new issue