diff --git a/scripts/build_tools/save_tools.ts b/scripts/build_tools/save_tools.ts index d54c0bb4..885e3c85 100644 --- a/scripts/build_tools/save_tools.ts +++ b/scripts/build_tools/save_tools.ts @@ -277,8 +277,8 @@ export async function saveToolsInNode(toolsOriginal: DirectoryEntry[]): Promise< const tools: DirectoryEntry[] = JSON.parse(JSON.stringify(toolsOriginal)); const toolsSaved: DirectoryEntry[] = []; for (const tool of tools) { - // Wait 250ms between tool uploads - await new Promise(resolve => setTimeout(resolve, 250)); + // Wait 50ms between tool uploads + await new Promise(resolve => setTimeout(resolve, 50)); // Read files const metadata: Metadata = JSON.parse(await Deno.readTextFile(join(tool.dir, "metadata.json"))); @@ -353,12 +353,6 @@ export async function saveToolsInNode(toolsOriginal: DirectoryEntry[]): Promise< args: [zipPath, '-d', zipPathFiles], }); await unzip.output(); - - // Enable flag to update reference files - // copy the unzipped __tool.json to the tool directory as .tool-dump.test.json - if (Deno.env.get("UPDATE_DUMP_FILES")) { - await Deno.copyFile(join(zipPathFiles, "__tool.json"), join(tool.dir, ".tool-dump.test.json")); - } } catch { console.error(`Error: Invalid zip file downloaded for ${tool.name}`); diff --git a/scripts/tests/tool-memory.test.ts b/scripts/tests/tool-memory.test.ts new file mode 100644 index 00000000..39ecb420 --- /dev/null +++ b/scripts/tests/tool-memory.test.ts @@ -0,0 +1,102 @@ +const code = Deno.readTextFileSync(import.meta.dirname + '/../../tools/memory/tool.ts'); +const metadata = JSON.parse(Deno.readTextFileSync(import.meta.dirname + '/../../tools/memory/metadata.json')); + +type INPUTS = { + data?: string; + general_prompt?: string; + specific_prompt?: string; + key?: string; +}; +const X_SHINKAI_TOOL_ID = `example-${Math.random().toString(36).substring(2, 15)}`; +const X_SHINKAI_APP_ID = `run-${Math.random().toString(36).substring(2, 15)}`; + +const base_url = Deno.env.get('SHINKAI_NODE_ADDR') ?? 'http://localhost:9950'; +const token = Deno.env.get('BEARER_TOKEN') ?? 'debug'; +const llm_provider = Deno.env.get('INITIAL_AGENT_NAMES') ? (Deno.env.get('INITIAL_AGENT_NAMES')??'').split(',')[0] : 'llama3_1_8b'; +async function runCommandTest(parameters: INPUTS) { + const response = await fetch(base_url + '/v2/code_execution', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + token, + 'x-shinkai-tool-id': X_SHINKAI_TOOL_ID, + 'x-shinkai-app-id': X_SHINKAI_APP_ID, + 'x-shinkai-llm-provider': llm_provider, + 'Content-Type': 'application/json; charset=utf-8', + }, + body: JSON.stringify({ + code, + tool_type: 'denodynamic', + llm_provider, + tools: metadata.tools || [], + parameters + }) + }); + + const data = await response.json(); + return data; +} + +export async function memoryTest() { + const expect = (data: any, message: string | undefined, errorStr: string | undefined) => { + if (!data) throw Error('[Check failed] ' + (message ?? '') + ' ' + (errorStr ?? '')); + else console.log('[Check passed] ' + (message ?? '')); + } + + const parametersA: INPUTS = { + data: 'Chile,[a] officially the Republic of Chile,[b] is a country in western South America. It is the southernmost country in the world and the closest to Antarctica, stretching along a narrow strip of land between the Andes Mountains and the Pacific Ocean. Chile had a population of 17.5 million as of the latest census in 2017 and has a territorial area of 756,102 square kilometers (291,933 sq mi),[10][3] sharing borders with Peru to the north, Bolivia to the northeast, Argentina to the east, and the Drake Passage to the south. The country also controls several Pacific islands, including Juan Fernández, Isla Salas y Gómez, Desventuradas, and Easter Island, and claims about 1,250,000 square kilometers (480,000 sq mi) of Antarctica as the Chilean Antarctic Territory.[nb 2] The capital and largest city of Chile is Santiago, and the national language is Spanish.', + } + const dataA= await runCommandTest(parametersA); + // console.log('================================\nExpected: Chile\n', dataA.generalMemory); + expect(dataA.generalMemory.match(/Chile/i), '0. Chile found', JSON.stringify(dataA)); + + const parametersB: INPUTS = { + data: 'Argentina,[C] officially the Argentine Republic,[A][D] is a country in the southern half of South America. Argentina covers an area of 2,780,400 km2 (1,073,500 sq mi),[B] making it the second-largest country in South America after Brazil, the fourth-largest country in the Americas, and the eighth-largest country in the world. It shares the bulk of the Southern Cone with Chile to the west, and is also bordered by Bolivia and Paraguay to the north, Brazil to the northeast, Uruguay and the South Atlantic Ocean to the east, and the Drake Passage to the south. Argentina is a federal state subdivided into twenty-three provinces, and one autonomous city, which is the federal capital and largest city of the nation, Buenos Aires. The provinces and the capital have their own constitutions, but exist under a federal system. Argentina claims sovereignty over the Falkland Islands, South Georgia and the South Sandwich Islands, the Southern Patagonian Ice Field, and a part of Antarctica.', + } + + const dataB = await runCommandTest(parametersB); + // console.log('================================\nExpected: Chile & Argentina\n', dataB.generalMemory); + expect(dataB.generalMemory.match(/Chile/i), '1. Chile found', JSON.stringify(dataB)); + expect(dataB.generalMemory.match(/Argentina/i), '2. Argentina found', JSON.stringify(dataB)); + + const parametersC: INPUTS = { + data: 'Spain,[f] officially the Kingdom of Spain,[a][g] is a country in Southwestern Europe with territories in North Africa.[12][h] Featuring the southernmost point of continental Europe, it is the largest country in Southern Europe and the fourth-most populous European Union member state. Spanning across the majority of the Iberian Peninsula, its territory also includes the Canary Islands, in the Eastern Atlantic Ocean, the Balearic Islands, in the Western Mediterranean Sea, and the autonomous cities of Ceuta and Melilla, in Africa. Peninsular Spain is bordered to the north by France, Andorra, and the Bay of Biscay; to the east and south by the Mediterranean Sea and Gibraltar; and to the west by Portugal and the Atlantic Ocean. Spain\'s capital and largest city is Madrid, and other major urban areas include Barcelona, Valencia, Seville, Zaragoza, Málaga, Murcia and Palma de Mallorca.', + key: 'spain' + }; + + const dataC = await runCommandTest(parametersC); + // console.log('================================\nExpected: Spain\n', dataC.specificMemory); + expect(dataC.specificMemory.match(/Spain/i), '3. Spain found', JSON.stringify(dataC)); + expect(!dataC.specificMemory.match(/Chile/i), '4. Chile not found', JSON.stringify(dataC)); + expect(!dataC.specificMemory.match(/Argentina/i), '5. Argentina not found', JSON.stringify(dataC)); + + const parametersD: INPUTS = { + data: 'France,[X] officially the French Republic,[XI] is a country located primarily in Western Europe. Its overseas regions and territories include French Guiana in South America, Saint Pierre and Miquelon in the North Atlantic, the French West Indies, and many islands in Oceania and the Indian Ocean, giving it one of the largest discontiguous exclusive economic zones in the world. Metropolitan France shares borders with Belgium and Luxembourg to the north, Germany to the northeast, Switzerland to the east, Italy and Monaco to the southeast, Andorra and Spain to the south, and a maritime border with the United Kingdom to the northwest. Its metropolitan area extends from the Rhine to the Atlantic Ocean and from the Mediterranean Sea to the English Channel and the North Sea. Its eighteen integral regions—five of which are overseas—span a combined area of 643,801 km2 (248,573 sq mi) and have a total population of nearly 68.4 million as of January 2024. France is a semi-presidential republic with its capital in Paris, the country\'s largest city and main cultural and economic centre.', // key: 'key' + key: 'france' + } + const dataD = await runCommandTest(parametersD); + // console.log('================================\nExpected: France\n', dataD.specificMemory); + expect(dataD.specificMemory.match(/France/i), '6. France found', JSON.stringify(dataD)); + + const parametersE: INPUTS = { + }; + const dataE = await runCommandTest(parametersE); + // console.log('================================\nExpected: Chile & Argentina\n', dataE.generalMemory); + expect(dataE.generalMemory.match(/Chile/i), '7. Chile found', JSON.stringify(dataE)); + expect(dataE.generalMemory.match(/Argentina/i), '8. Argentina found', JSON.stringify(dataE)); + expect(!dataE.specificMemory.match(/Spain/i), '9. Spain not found', JSON.stringify(dataE)); + + const parametersF: INPUTS = { + key: 'spain' + }; + const dataF = await runCommandTest(parametersF); + // console.log('================================\nExpected: Spain\n', dataF.specificMemory); + expect(dataF.specificMemory.match(/Spain/i), '10. Spain found', JSON.stringify(dataF)); + + const parametersG: INPUTS = { + key: 'england' + }; + const dataG = await runCommandTest(parametersG); + // console.log('================================\nExpected: None\n', dataG.specificMemory); + expect(dataG.specificMemory === '' || dataG.specificMemory === null, '11. Empty memory', JSON.stringify(dataG)); +} + diff --git a/scripts/zip.test.ts b/scripts/zip.test.ts index 312aac05..435cec78 100644 --- a/scripts/zip.test.ts +++ b/scripts/zip.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "jsr:@std/assert"; +import { assertEquals, equal } from "jsr:@std/assert"; import { getMetadata, processToolsDirectory, saveToolsInNode } from "./build_tools/save_tools.ts"; import { join } from "https://deno.land/std/path/mod.ts"; import { exists } from "https://deno.land/std/fs/mod.ts"; @@ -53,9 +53,21 @@ Deno.test("Compare shinkai-node generated ZIP __tool.json vs .tool-dump.test.jso // Embeddings might change. zipToolData.content[0].embedding = []; toolDumpData.content[0].embedding = []; - assertEquals(zipToolData, toolDumpData); + + // Enable flag to update reference files + // copy the unzipped __tool.json to the tool directory as .tool-dump.test.json + if (Deno.env.get("UPDATE_DUMP_FILES")) { + if (!equal(zipToolData, toolDumpData)) { + console.log('Updating reference file:', entryx.name); + await Deno.copyFile(join(zipDir, "__tool.json"), join(toolDir, ".tool-dump.test.json")); + } + } else { + assertEquals(zipToolData, toolDumpData); + } } - }); +// Deno.test("Check if memory is working", async () => { +// await memoryTest(); +// }); diff --git a/tools/memory/.tool-dump.test.json b/tools/memory/.tool-dump.test.json index 8ce2f351..47778ccd 100644 --- a/tools/memory/.tool-dump.test.json +++ b/tools/memory/.tool-dump.test.json @@ -1 +1 @@ -{"type":"Deno","content":[{"name":"Memory Management","homepage":null,"author":"@@official.shinkai","version":"1.0.0","js_code":"import { shinkaiSqliteQueryExecutor } from './shinkai-local-tools.ts';\nimport { shinkaiLlmPromptProcessor } from './shinkai-local-tools.ts';\n\ntype CONFIG = {};\ntype INPUTS = {\n data?: string;\n general_prompt?: string;\n specific_prompt?: string;\n key?: string;\n};\ntype OUTPUT = {\n generalMemory: string;\n specificMemory: string;\n};\n\nconst createTable = async (): Promise => {\n // Create table if not exists\n const createTableQuery = `\n CREATE TABLE IF NOT EXISTS memory_table (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n date DATETIME DEFAULT CURRENT_TIMESTAMP,\n key TEXT,\n memory TEXT\n );\n `;\n await shinkaiSqliteQueryExecutor({ query: createTableQuery });\n}\n\nconst getGeneralMemory = async (): Promise => {\n const fetchGeneralMemoryQuery = `\n SELECT id, key, memory\n FROM memory_table\n where key is null\n `;\n const fetchGeneralMemory = await shinkaiSqliteQueryExecutor({ query: fetchGeneralMemoryQuery });\n\n if (fetchGeneralMemory.result.length) {\n return fetchGeneralMemory.result[0];\n }\n return null;\n}\n\nconst getSpecificMemory = async (key: string): Promise => {\n const fetchSpecificMemoryQuery = `\n SELECT id, key, memory\n FROM memory_table\n where key = ?\n `;\n const fetchSpecificMemory = await shinkaiSqliteQueryExecutor({ query: fetchSpecificMemoryQuery, params: [key] });\n\n if (fetchSpecificMemory.result.length) {\n return fetchSpecificMemory.result[0];\n }\n return null;\n}\n\nconst generatePrompt = async (\n previousMemory: null | {id: number, key: string, memory: string},\n general_prompt: string,\n data: string): Promise => {\n let generalPrompt = `\nYou must generate memories, so we can recall new and past interactions.\nBased on the rules, you must generate the output.\nWe should merge new and past interactions into a single memory.\nKeep the most important information only.\n\nThese are some sections you must understand:\n * rules tag: has the rules you must follow to generate the output.\\n`;\n if (previousMemory) generalPrompt += `. * previous_interactions tag: has entire previous interaction memory\\n`;\n generalPrompt += `. * input tag: has the new data to for creating newa memories.\n\n\n ${general_prompt}\n\n `\n if (previousMemory)\n generalPrompt += `\n\n ${previousMemory.memory}\n\n `;\n\n generalPrompt += `\n\n ${data}\n\n `;\n return generalPrompt;\n}\n\nexport async function run(config: CONFIG, inputs: INPUTS): Promise {\n const {\n data,\n general_prompt = 'Important information to remember from this interaction',\n specific_prompt = 'Important information to remember from this interaction',\n key\n } = inputs;\n\n await createTable();\n\n let generalMemory = '';\n let specificMemory = '';\n // If no data provided, just return existing memories\n if (!data) {\n const existingGeneralMemory = await getGeneralMemory();\n const existingSpecificMemory = key ? await getSpecificMemory(key) : null;\n\n return {\n generalMemory: existingGeneralMemory?.memory || '',\n specificMemory: existingSpecificMemory?.memory || ''\n };\n }\n\n // Update General Memory\n const previousGeneralMemory = await getGeneralMemory();\n const generalPrompt = await generatePrompt(previousGeneralMemory, general_prompt, data);\n const generalResponse = await shinkaiLlmPromptProcessor({ format: 'text', prompt: generalPrompt });\n generalMemory = generalResponse.message;\n\n if (previousGeneralMemory) {\n const generalUpdateQuery = `\n UPDATE memory_table SET memory = ?\n WHERE id = ?\n `;\n await shinkaiSqliteQueryExecutor({\n query: generalUpdateQuery, params: [generalMemory, \"\"+ previousGeneralMemory.id]\n });\n } else {\n const generalInsertQuery = `\n INSERT INTO memory_table (memory)\n VALUES (?);\n `;\n await shinkaiSqliteQueryExecutor({ query: generalInsertQuery, params: [generalMemory]});\n }\n\n // Update specific memory\n if (key) {\n const previousSpecificMemory = await getSpecificMemory(key);\n const specificPrompt = await generatePrompt(previousSpecificMemory, specific_prompt, data);\n const specificResponse = await shinkaiLlmPromptProcessor({ format: 'text', prompt: specificPrompt });\n specificMemory = specificResponse.message;\n\n if (previousSpecificMemory) {\n const specificUpdateQuery = `\n UPDATE memory_table SET memory = ?\n WHERE id = ?\n `;\n await shinkaiSqliteQueryExecutor({\n query: specificUpdateQuery,\n params: [specificMemory, \"\"+previousSpecificMemory.id]\n });\n } else {\n const specificInsertQuery = `\n INSERT INTO memory_table (key, memory)\n VALUES (?, ?);\n `;\n await shinkaiSqliteQueryExecutor({\n query: specificInsertQuery,\n params: [key, specificMemory]\n });\n }\n }\n return {generalMemory, specificMemory};\n}","tools":["local:::__official_shinkai:::shinkai_sqlite_query_executor","local:::__official_shinkai:::shinkai_llm_prompt_processor"],"config":[],"description":"Handles memory storage and retrieval using a SQLite database.","keywords":["memory","remember","management","recall","smart","agent"],"input_args":{"type":"object","properties":{"key":{"type":"string","description":"The key for specific memory retrieval"},"data":{"type":"string","description":"The data to process for memory management, if not provided, the tool will return existing memories"},"specific_prompt":{"type":"string","description":"The specific prompt for generating memories"},"general_prompt":{"type":"string","description":"The general prompt for generating memories"}},"required":[]},"output_arg":{"json":""},"activated":false,"embedding":[0.111937836,0.74089676,-0.12515827,-0.4942115,-0.25368002,-0.056685545,-0.809049,0.13576964,0.24071948,-0.02976514,-0.47945547,0.99631506,-0.08526395,-0.08666597,0.4112748,-0.34366244,0.28832552,0.8723968,-1.8125455,-0.06927829,0.3785739,0.44344294,0.07779312,-0.13623443,-0.198864,0.104956515,0.07281388,-0.64128476,-0.8674953,-2.212524,0.23000515,0.8036334,-0.32317746,0.08531259,0.06165708,-0.5280139,-0.27165845,0.2298748,-1.0989754,-0.4352798,0.060814954,0.20959929,-0.6922089,0.14129707,0.3951045,-0.2094253,0.28318685,0.03080573,0.6607957,0.44034725,-0.3435076,-0.6636055,-0.49609208,0.031462383,-0.46229592,-0.11738148,-0.2673628,-0.12942854,-0.13213739,-0.56978685,-0.49050757,-0.033710778,-3.0540113,-0.35828966,0.4477928,0.11795497,0.6029618,0.03019675,-0.13932246,-0.116601504,-0.5129008,0.22289489,0.001567632,0.43084416,0.010488745,-0.65866005,0.5444606,-0.16649291,0.5965793,-0.56898767,0.5876942,0.68564534,0.057885304,-0.11264849,-0.36528912,0.22000486,-0.5172195,-0.034327753,0.20772007,0.10029636,-0.11128229,-0.35220385,0.042001657,-0.09486667,-0.49064255,0.5016759,0.5429697,0.53100413,0.67431355,2.9295228,0.6061498,-0.0052450076,1.1123242,-0.5553272,0.58578116,-0.396771,-0.0043778606,-0.2641139,-0.18956795,-0.04787702,0.5651373,0.04069993,0.10868324,0.3633739,0.31594813,-0.14918776,-0.90317976,0.014140373,0.09339001,0.4759403,-1.2680333,0.15725,-0.8017013,-0.072784826,-0.2027275,-0.248173,-0.47450486,0.35822383,0.089721486,-0.8434949,0.17163289,-0.60608387,-1.5258477,-0.092011675,0.15190832,0.029228859,0.09951277,-1.0531722,-0.8432071,-1.2045652,0.22168328,-1.8012422,1.257536,0.3710498,0.82652575,1.064956,-0.07364569,-0.08260139,-0.32399875,-0.2577161,0.35024714,0.3111611,0.20929018,-0.17805251,0.8778527,-0.086034104,0.20599265,0.22164212,-0.5891931,-0.16454726,-0.681477,-0.039246723,0.36468878,0.25415224,0.87371546,-1.1239837,-0.07615483,-0.40367773,0.4243997,-0.22388089,-0.036021143,0.070409395,0.051366255,0.23837432,0.1067911,-0.5528012,0.23844612,0.0024840161,-0.080963686,-0.751668,-0.0518061,0.65869457,-0.27564827,-0.7393524,-0.30968362,-0.65195227,0.98374283,-0.09401393,0.04650405,0.98091817,-0.44676092,1.0435079,-0.8741324,0.5287304,-0.24334532,-0.3493321,-0.09778318,0.6641216,0.6423144,-0.56234205,-0.34151262,-0.6116001,-0.15467788,0.09333934,0.65145075,-0.36601484,-0.3339522,0.050637126,0.41447765,-0.524192,-0.22862165,-0.5204915,0.31959406,0.34199706,0.31173804,0.29922813,0.04193517,0.65267223,0.23422848,0.9934678,0.8802714,-0.06621673,0.4821114,-0.5185084,-0.2266081,-0.7151844,-0.047955215,-0.44060528,-0.43365583,-0.5805751,0.23047966,0.8946043,0.8859454,0.15834664,0.5712067,0.035358794,0.26624507,0.36157402,0.4208076,-0.784662,0.5708339,0.2283394,-0.415547,-0.34696782,0.041988354,-0.25026345,0.35980847,0.3587914,0.43452892,1.2345545,0.8542529,0.43813187,-0.117733255,0.3318603,0.37991884,0.049697533,-1.7096437,0.12233965,-0.33652085,0.3047968,0.4880944,0.005683899,0.48125106,1.0229537,0.42820078,0.1880018,-0.9280594,0.3796176,0.1298208,-0.008567318,-0.70267904,1.1864583,-0.43549532,-0.4198141,-0.07940441,-0.76350194,0.7171461,-0.42064208,-0.34265658,-0.36742902,0.12565221,0.12214184,0.9031202,-0.023328215,-0.048132613,-0.167081,-0.33202252,0.27307153,0.20823562,0.9203475,-0.19106463,-0.35383615,-0.7042749,0.85880107,1.4127065,1.3556606,0.24954379,0.52383125,0.82653356,-0.12702428,0.067642696,0.41572976,-0.33709046,0.09285276,-0.18625477,0.002191268,0.55797386,0.38143012,-0.35884345,0.2095905,-1.2938337,0.13288312,0.13235816,-0.22439846,0.3647071,-0.53730625,0.47953555,1.4558412,-0.17408374,-2.000823,-0.6611798,0.23612174,-0.14356868,-0.20251471,-0.13713701,0.20885682,-0.7435712,0.25030732,0.543007,1.3142625,0.44838735,-0.8564536,-0.75826186,-0.43299165,0.9422145,0.4068938,0.554768,-0.13111398,-0.42246768,-0.07616982,0.5571903,1.2492561,0.48375618,0.051635597,-0.21371765,0.35021627,-0.5410304,-1.1716174,0.5095912,-0.62912905,-0.37279865,0.41224012,0.59948826,-0.3992,0.7356437,1.1001694,0.24999188,0.24701267,-1.1759105,1.4969252,-0.020239882,-0.31295842,-0.93301684,0.77810735,-0.3480711,-0.41037232,0.65593684,-0.14069337,-0.28005433,-0.22853693,-0.022693757,-0.26941186,0.16493139,0.45033836,0.60703117,0.5285942,0.12087938,0.25766528,-0.34744433,0.104460105,1.0776262,0.4048118,-1.3009826,-0.40838832],"result":{"type":"object","properties":{"generalMemory":{"description":"The updated general memory","nullable":true,"type":"string"},"specificMemory":{"description":"The updated specific memory","nullable":true,"type":"string"}},"required":[]},"sql_tables":[{"name":"memory_table","definition":"CREATE TABLE IF NOT EXISTS memory_table (id INTEGER PRIMARY KEY AUTOINCREMENT, date DATETIME DEFAULT CURRENT_TIMESTAMP, key TEXT, memory TEXT)"}],"sql_queries":[{"name":"Get general memory","query":"SELECT id, key, memory FROM memory_table WHERE key IS NULL"},{"name":"Get specific memory","query":"SELECT id, key, memory FROM memory_table WHERE key = ?"},{"name":"Update memory","query":"UPDATE memory_table SET memory = ? WHERE id = ?"}],"file_inbox":null,"oauth":null,"assets":null,"runner":"any","operating_system":["linux","macos","windows"],"tool_set":""},false]} \ No newline at end of file +{"type":"Deno","content":[{"name":"Memory Management","homepage":null,"author":"@@official.shinkai","version":"1.0.0","js_code":"import { shinkaiSqliteQueryExecutor as shinkaiSqliteQueryExecutor_ } from './shinkai-local-tools.ts';\nimport { shinkaiLlmPromptProcessor } from './shinkai-local-tools.ts';\n\nconst shinkaiSqliteQueryExecutor = (params: any) => {\n console.log('shinkaiSqliteQueryExecutor', params);\n return shinkaiSqliteQueryExecutor_(params);\n}\n\ntype CONFIG = {};\ntype INPUTS = {\n data?: string;\n general_prompt?: string;\n specific_prompt?: string;\n key?: string;\n};\ntype OUTPUT = {\n generalMemory: string;\n specificMemory: string;\n};\n\nconst createTable = async (): Promise => {\n // Create table if not exists\n const createTableQuery = `\n CREATE TABLE IF NOT EXISTS memory_table (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n date DATETIME DEFAULT CURRENT_TIMESTAMP,\n key TEXT,\n memory TEXT\n );\n `;\n await shinkaiSqliteQueryExecutor({ query: createTableQuery });\n}\n\nconst getGeneralMemory = async (): Promise => {\n const fetchGeneralMemoryQuery = `\n SELECT id, key, memory\n FROM memory_table\n where key is null\n `;\n const fetchGeneralMemory = await shinkaiSqliteQueryExecutor({ query: fetchGeneralMemoryQuery });\n\n if (fetchGeneralMemory.result.length) {\n return fetchGeneralMemory.result[0];\n }\n return null;\n}\n\nconst getSpecificMemory = async (key: string): Promise => {\n const fetchSpecificMemoryQuery = `\n SELECT id, key, memory\n FROM memory_table\n where key = ?\n `;\n const fetchSpecificMemory = await shinkaiSqliteQueryExecutor({ query: fetchSpecificMemoryQuery, params: [key] });\n\n if (fetchSpecificMemory.result.length) {\n return fetchSpecificMemory.result[0];\n }\n return null;\n}\n\nconst generatePrompt = async (\n previousMemory: null | {id: number, key: string, memory: string},\n general_prompt: string,\n data: string): Promise => {\n let generalPrompt = `\n* You must generate memories, so we can recall new and past interactions.\n* Retrive past memories if there are any, and merge them with the new data.\n* We should merge new and past interactions, into a single memory.\n* We can restructure the memory to make it consistent and ordered.\n* Keep the most important information only.\n* Based on the rules tag, you must generate the output.\n\nUse \"##\" to write and identify main topics\nUse \"#\" to identify titles of definitions\n\nOnly output the new memory, without comments, suggestions or how it was generated.\n\nThis is an example on how to structure the memory, not the fields you must use.\n\\`\\`\\`\n# Location\n## NY: Latitude: 40.7128, Longitude: -74.0060\n## CO: Latitude: -33.4569, Longitude: -70.6483\n- CO has borders with Perú and Bolivia\n\n# Known People\n## John: 30 years old\n## Jane: 25 years old\n## Peter: is from Europe.\n- John and Jane are friends \n\\`\\`\\`\n\nThese are some sections you must understand:\n * rules tag: has the rules you must follow to generate the output.\\n`;\n if (previousMemory) generalPrompt += `. * previous_interactions tag: has entire previous interaction memory\\n`;\n generalPrompt += `. * input tag: has the new data to for creating new memories.\n\n\n ${general_prompt}\n\n `\n if (previousMemory)\n generalPrompt += `\n\n ${previousMemory.memory}\n\n `;\n\n generalPrompt += `\n\n ${data}\n\n `;\n return generalPrompt;\n}\n\nexport async function run(config: CONFIG, inputs: INPUTS): Promise {\n const {\n data,\n general_prompt = 'Synthesize important information to remember from this interaction',\n specific_prompt = 'Synthesize important information to remember from this interaction',\n key\n } = inputs;\n\n await createTable();\n // If no data provided, just return existing memories\n if (!data) {\n const existingGeneralMemory = await getGeneralMemory();\n const existingSpecificMemory = key ? await getSpecificMemory(key) : null;\n\n return {\n generalMemory: existingGeneralMemory?.memory || '',\n specificMemory: existingSpecificMemory?.memory || ''\n };\n }\n\n if (!key) {\n // Update General Memory\n const previousGeneralMemory = await getGeneralMemory();\n const generalPrompt = await generatePrompt(previousGeneralMemory, general_prompt, data);\n const generalResponse = await shinkaiLlmPromptProcessor({ format: 'text', prompt: generalPrompt });\n const generalMemory = generalResponse.message;\n\n if (previousGeneralMemory) {\n const generalUpdateQuery = `\n UPDATE memory_table SET memory = ?\n WHERE id = ?\n `;\n await shinkaiSqliteQueryExecutor({\n query: generalUpdateQuery, params: [generalMemory, \"\"+ previousGeneralMemory.id]\n });\n } else {\n const generalInsertQuery = `\n INSERT INTO memory_table (memory)\n VALUES (?);\n `;\n await shinkaiSqliteQueryExecutor({ query: generalInsertQuery, params: [generalMemory]});\n }\n return {generalMemory, specificMemory: ''};\n } else {\n // Update specific memory\n const previousSpecificMemory = await getSpecificMemory(key);\n const specificPrompt = await generatePrompt(previousSpecificMemory, specific_prompt, data);\n const specificResponse = await shinkaiLlmPromptProcessor({ format: 'text', prompt: specificPrompt });\n const specificMemory = specificResponse.message;\n\n if (previousSpecificMemory) {\n const specificUpdateQuery = `\n UPDATE memory_table SET memory = ?\n WHERE id = ?\n `;\n await shinkaiSqliteQueryExecutor({\n query: specificUpdateQuery,\n params: [specificMemory, \"\"+previousSpecificMemory.id]\n });\n } else {\n const specificInsertQuery = `\n INSERT INTO memory_table (key, memory)\n VALUES (?, ?);\n `;\n await shinkaiSqliteQueryExecutor({\n query: specificInsertQuery,\n params: [key, specificMemory]\n });\n }\n return {generalMemory: '', specificMemory};\n }\n}","tools":["local:::__official_shinkai:::shinkai_sqlite_query_executor","local:::__official_shinkai:::shinkai_llm_prompt_processor"],"config":[],"description":"Handles memory storage and retrieval using a SQLite database.","keywords":["memory","remember","management","recall","smart","agent"],"input_args":{"type":"object","properties":{"key":{"type":"string","description":"The key for specific memory retrieval"},"general_prompt":{"type":"string","description":"The general prompt for generating memories"},"specific_prompt":{"type":"string","description":"The specific prompt for generating memories"},"data":{"type":"string","description":"The data to process for memory management, if not provided, the tool will return existing memories"}},"required":[]},"output_arg":{"json":""},"activated":false,"embedding":[0.111937836,0.74089676,-0.12515827,-0.4942115,-0.25368002,-0.056685545,-0.809049,0.13576964,0.24071948,-0.02976514,-0.47945547,0.99631506,-0.08526395,-0.08666597,0.4112748,-0.34366244,0.28832552,0.8723968,-1.8125455,-0.06927829,0.3785739,0.44344294,0.07779312,-0.13623443,-0.198864,0.104956515,0.07281388,-0.64128476,-0.8674953,-2.212524,0.23000515,0.8036334,-0.32317746,0.08531259,0.06165708,-0.5280139,-0.27165845,0.2298748,-1.0989754,-0.4352798,0.060814954,0.20959929,-0.6922089,0.14129707,0.3951045,-0.2094253,0.28318685,0.03080573,0.6607957,0.44034725,-0.3435076,-0.6636055,-0.49609208,0.031462383,-0.46229592,-0.11738148,-0.2673628,-0.12942854,-0.13213739,-0.56978685,-0.49050757,-0.033710778,-3.0540113,-0.35828966,0.4477928,0.11795497,0.6029618,0.03019675,-0.13932246,-0.116601504,-0.5129008,0.22289489,0.001567632,0.43084416,0.010488745,-0.65866005,0.5444606,-0.16649291,0.5965793,-0.56898767,0.5876942,0.68564534,0.057885304,-0.11264849,-0.36528912,0.22000486,-0.5172195,-0.034327753,0.20772007,0.10029636,-0.11128229,-0.35220385,0.042001657,-0.09486667,-0.49064255,0.5016759,0.5429697,0.53100413,0.67431355,2.9295228,0.6061498,-0.0052450076,1.1123242,-0.5553272,0.58578116,-0.396771,-0.0043778606,-0.2641139,-0.18956795,-0.04787702,0.5651373,0.04069993,0.10868324,0.3633739,0.31594813,-0.14918776,-0.90317976,0.014140373,0.09339001,0.4759403,-1.2680333,0.15725,-0.8017013,-0.072784826,-0.2027275,-0.248173,-0.47450486,0.35822383,0.089721486,-0.8434949,0.17163289,-0.60608387,-1.5258477,-0.092011675,0.15190832,0.029228859,0.09951277,-1.0531722,-0.8432071,-1.2045652,0.22168328,-1.8012422,1.257536,0.3710498,0.82652575,1.064956,-0.07364569,-0.08260139,-0.32399875,-0.2577161,0.35024714,0.3111611,0.20929018,-0.17805251,0.8778527,-0.086034104,0.20599265,0.22164212,-0.5891931,-0.16454726,-0.681477,-0.039246723,0.36468878,0.25415224,0.87371546,-1.1239837,-0.07615483,-0.40367773,0.4243997,-0.22388089,-0.036021143,0.070409395,0.051366255,0.23837432,0.1067911,-0.5528012,0.23844612,0.0024840161,-0.080963686,-0.751668,-0.0518061,0.65869457,-0.27564827,-0.7393524,-0.30968362,-0.65195227,0.98374283,-0.09401393,0.04650405,0.98091817,-0.44676092,1.0435079,-0.8741324,0.5287304,-0.24334532,-0.3493321,-0.09778318,0.6641216,0.6423144,-0.56234205,-0.34151262,-0.6116001,-0.15467788,0.09333934,0.65145075,-0.36601484,-0.3339522,0.050637126,0.41447765,-0.524192,-0.22862165,-0.5204915,0.31959406,0.34199706,0.31173804,0.29922813,0.04193517,0.65267223,0.23422848,0.9934678,0.8802714,-0.06621673,0.4821114,-0.5185084,-0.2266081,-0.7151844,-0.047955215,-0.44060528,-0.43365583,-0.5805751,0.23047966,0.8946043,0.8859454,0.15834664,0.5712067,0.035358794,0.26624507,0.36157402,0.4208076,-0.784662,0.5708339,0.2283394,-0.415547,-0.34696782,0.041988354,-0.25026345,0.35980847,0.3587914,0.43452892,1.2345545,0.8542529,0.43813187,-0.117733255,0.3318603,0.37991884,0.049697533,-1.7096437,0.12233965,-0.33652085,0.3047968,0.4880944,0.005683899,0.48125106,1.0229537,0.42820078,0.1880018,-0.9280594,0.3796176,0.1298208,-0.008567318,-0.70267904,1.1864583,-0.43549532,-0.4198141,-0.07940441,-0.76350194,0.7171461,-0.42064208,-0.34265658,-0.36742902,0.12565221,0.12214184,0.9031202,-0.023328215,-0.048132613,-0.167081,-0.33202252,0.27307153,0.20823562,0.9203475,-0.19106463,-0.35383615,-0.7042749,0.85880107,1.4127065,1.3556606,0.24954379,0.52383125,0.82653356,-0.12702428,0.067642696,0.41572976,-0.33709046,0.09285276,-0.18625477,0.002191268,0.55797386,0.38143012,-0.35884345,0.2095905,-1.2938337,0.13288312,0.13235816,-0.22439846,0.3647071,-0.53730625,0.47953555,1.4558412,-0.17408374,-2.000823,-0.6611798,0.23612174,-0.14356868,-0.20251471,-0.13713701,0.20885682,-0.7435712,0.25030732,0.543007,1.3142625,0.44838735,-0.8564536,-0.75826186,-0.43299165,0.9422145,0.4068938,0.554768,-0.13111398,-0.42246768,-0.07616982,0.5571903,1.2492561,0.48375618,0.051635597,-0.21371765,0.35021627,-0.5410304,-1.1716174,0.5095912,-0.62912905,-0.37279865,0.41224012,0.59948826,-0.3992,0.7356437,1.1001694,0.24999188,0.24701267,-1.1759105,1.4969252,-0.020239882,-0.31295842,-0.93301684,0.77810735,-0.3480711,-0.41037232,0.65593684,-0.14069337,-0.28005433,-0.22853693,-0.022693757,-0.26941186,0.16493139,0.45033836,0.60703117,0.5285942,0.12087938,0.25766528,-0.34744433,0.104460105,1.0776262,0.4048118,-1.3009826,-0.40838832],"result":{"type":"object","properties":{"generalMemory":{"description":"The updated general memory","nullable":true,"type":"string"},"specificMemory":{"description":"The updated specific memory","nullable":true,"type":"string"}},"required":[]},"sql_tables":[{"name":"memory_table","definition":"CREATE TABLE IF NOT EXISTS memory_table (id INTEGER PRIMARY KEY AUTOINCREMENT, date DATETIME DEFAULT CURRENT_TIMESTAMP, key TEXT, memory TEXT)"}],"sql_queries":[{"name":"Get general memory","query":"SELECT id, key, memory FROM memory_table WHERE key IS NULL"},{"name":"Get specific memory","query":"SELECT id, key, memory FROM memory_table WHERE key = ?"},{"name":"Update memory","query":"UPDATE memory_table SET memory = ? WHERE id = ?"}],"file_inbox":null,"oauth":null,"assets":null,"runner":"any","operating_system":["linux","macos","windows"],"tool_set":""},false]} \ No newline at end of file diff --git a/tools/memory/tool.ts b/tools/memory/tool.ts index 36052ac2..1c692018 100644 --- a/tools/memory/tool.ts +++ b/tools/memory/tool.ts @@ -1,6 +1,11 @@ -import { shinkaiSqliteQueryExecutor } from './shinkai-local-tools.ts'; +import { shinkaiSqliteQueryExecutor as shinkaiSqliteQueryExecutor_ } from './shinkai-local-tools.ts'; import { shinkaiLlmPromptProcessor } from './shinkai-local-tools.ts'; +const shinkaiSqliteQueryExecutor = (params: any) => { + console.log('shinkaiSqliteQueryExecutor', params); + return shinkaiSqliteQueryExecutor_(params); +} + type CONFIG = {}; type INPUTS = { data?: string; @@ -59,15 +64,36 @@ const generatePrompt = async ( general_prompt: string, data: string): Promise => { let generalPrompt = ` -You must generate memories, so we can recall new and past interactions. -Based on the rules, you must generate the output. -We should merge new and past interactions into a single memory. -Keep the most important information only. +* You must generate memories, so we can recall new and past interactions. +* Retrive past memories if there are any, and merge them with the new data. +* We should merge new and past interactions, into a single memory. +* We can restructure the memory to make it consistent and ordered. +* Keep the most important information only. +* Based on the rules tag, you must generate the output. + +Use "##" to write and identify main topics +Use "#" to identify titles of definitions + +Only output the new memory, without comments, suggestions or how it was generated. + +This is an example on how to structure the memory, not the fields you must use. +\`\`\` +# Location +## NY: Latitude: 40.7128, Longitude: -74.0060 +## CO: Latitude: -33.4569, Longitude: -70.6483 +- CO has borders with Perú and Bolivia + +# Known People +## John: 30 years old +## Jane: 25 years old +## Peter: is from Europe. +- John and Jane are friends +\`\`\` These are some sections you must understand: * rules tag: has the rules you must follow to generate the output.\n`; if (previousMemory) generalPrompt += `. * previous_interactions tag: has entire previous interaction memory\n`; - generalPrompt += `. * input tag: has the new data to for creating newa memories. + generalPrompt += `. * input tag: has the new data to for creating new memories. ${general_prompt} @@ -91,15 +117,12 @@ These are some sections you must understand: export async function run(config: CONFIG, inputs: INPUTS): Promise { const { data, - general_prompt = 'Important information to remember from this interaction', - specific_prompt = 'Important information to remember from this interaction', + general_prompt = 'Synthesize important information to remember from this interaction', + specific_prompt = 'Synthesize important information to remember from this interaction', key } = inputs; await createTable(); - - let generalMemory = ''; - let specificMemory = ''; // If no data provided, just return existing memories if (!data) { const existingGeneralMemory = await getGeneralMemory(); @@ -111,34 +134,35 @@ export async function run(config: CONFIG, inputs: INPUTS): Promise { }; } - // Update General Memory - const previousGeneralMemory = await getGeneralMemory(); - const generalPrompt = await generatePrompt(previousGeneralMemory, general_prompt, data); - const generalResponse = await shinkaiLlmPromptProcessor({ format: 'text', prompt: generalPrompt }); - generalMemory = generalResponse.message; - - if (previousGeneralMemory) { - const generalUpdateQuery = ` - UPDATE memory_table SET memory = ? - WHERE id = ? + if (!key) { + // Update General Memory + const previousGeneralMemory = await getGeneralMemory(); + const generalPrompt = await generatePrompt(previousGeneralMemory, general_prompt, data); + const generalResponse = await shinkaiLlmPromptProcessor({ format: 'text', prompt: generalPrompt }); + const generalMemory = generalResponse.message; + + if (previousGeneralMemory) { + const generalUpdateQuery = ` + UPDATE memory_table SET memory = ? + WHERE id = ? + `; + await shinkaiSqliteQueryExecutor({ + query: generalUpdateQuery, params: [generalMemory, ""+ previousGeneralMemory.id] + }); + } else { + const generalInsertQuery = ` + INSERT INTO memory_table (memory) + VALUES (?); `; - await shinkaiSqliteQueryExecutor({ - query: generalUpdateQuery, params: [generalMemory, ""+ previousGeneralMemory.id] - }); + await shinkaiSqliteQueryExecutor({ query: generalInsertQuery, params: [generalMemory]}); + } + return {generalMemory, specificMemory: ''}; } else { - const generalInsertQuery = ` - INSERT INTO memory_table (memory) - VALUES (?); - `; - await shinkaiSqliteQueryExecutor({ query: generalInsertQuery, params: [generalMemory]}); - } - - // Update specific memory - if (key) { + // Update specific memory const previousSpecificMemory = await getSpecificMemory(key); const specificPrompt = await generatePrompt(previousSpecificMemory, specific_prompt, data); const specificResponse = await shinkaiLlmPromptProcessor({ format: 'text', prompt: specificPrompt }); - specificMemory = specificResponse.message; + const specificMemory = specificResponse.message; if (previousSpecificMemory) { const specificUpdateQuery = ` @@ -159,6 +183,6 @@ export async function run(config: CONFIG, inputs: INPUTS): Promise { params: [key, specificMemory] }); } + return {generalMemory: '', specificMemory}; } - return {generalMemory, specificMemory}; } \ No newline at end of file diff --git a/tools/smart-search/.tool-dump.test.json b/tools/smart-search/.tool-dump.test.json index caf6ae63..c692fc30 100644 --- a/tools/smart-search/.tool-dump.test.json +++ b/tools/smart-search/.tool-dump.test.json @@ -1 +1 @@ -{"type":"Deno","content":[{"name":"Smart Search Engine","homepage":null,"author":"@@official.shinkai","version":"1.0.0","js_code":"import { googleSearch, shinkaiLlmPromptProcessor, downloadPages } from './shinkai-local-tools.ts';\n\ntype CONFIG = {\n searchEngineApiKey?: string;\n searchEngine?: SearchEngine;\n maxSources?: number;\n}\ntype INPUTS = {\n question: string;\n};\ntype OUTPUT = {\n response: string;\n sources: SmartSearchSourcePage[];\n statements: SmartSearchStatement[];\n}\ntype PREFFERED_SOURCES = 'WIKIPEDIA'|'WOLFRAMALPHA'|'OTHER';\n\ntype SearchQueryConversion = {\n \"origin_question\": string;\n \"preferred_sources\": PREFFERED_SOURCES[];\n \"search_query\": string\n}\n\ntype SearchResult = {\n title: string;\n description: string;\n url: string;\n}\n\ntype SmartSearchSource = SearchResult | string;\ntype SearchEngine = 'DUCKDUCKGO' | 'GOOGLE' | 'BRAVE';\n\nexport interface SmartSearchSourcePage {\n id: number;\n url: string;\n markdown?: string;\n title: string;\n}\n\nexport interface SmartSearchStatement {\n sourceId: number;\n sourceTitle: string;\n extractedFacts: {\n statement: string;\n relevance: 'DIRECT_ANSWER' | 'HIGHLY_RELEVANT' | 'SOMEWHAT_RELEVANT' | 'TANGENTIAL' | 'NOT_RELEVANT';\n }[];\n}\nexport interface SmartSearchGenerationContext {\n originalQuestion: string;\n statements: SmartSearchStatement[];\n sources: SmartSearchSourcePage[];\n}\n\nconst answerGenerator = (context: SmartSearchGenerationContext): string => `\n# Smart Search Answer Generation Instructions\nYou are a sophisticated scientific communication assistant specialized in transforming extracted research statements into comprehensive, accessible, and precisely cited explanations.Your primary objective is to synthesize complex information from multiple sources into a clear, authoritative answer that maintains absolute fidelity to the source material. Think of yourself as an academic translator - your role is to take fragmented scientific statements and weave them into a coherent narrative that is both intellectually rigorous and engaging, ensuring that every substantive claim is meticulously attributed to its original source. Approach each question as an opportunity to provide a deep, nuanced understanding that goes beyond surface-level explanation, while maintaining strict scholarly integrity.\n## Input JSON Interfaces and Definitions\n\n\\`\\`\\`typescript\n// Source Page Interface\nexport interface SmartSearchSourcePage {\n id: number; // Unique identifier for the source\n url: string; // Full URL of the source\n markdown: string; // Full text content of the source page\n title: string; // Title of the source page\n}\n\n// Statement Interface with Detailed Relevance Levels\nexport interface SmartSearchStatement {\n sourceId: number; // ID of the source this statement comes from\n sourceTitle: string; // Title of the source\n extractedFacts: {\n statement: string; // Exact verbatim text from the source\n relevance: 'DIRECT_ANSWER' \n | 'HIGHLY_RELEVANT' \n | 'SOMEWHAT_RELEVANT' \n | 'TANGENTIAL' \n | 'NOT_RELEVANT'; // Relevance classification\n }[];\n}\n\n// Complete Input JSON Structure\ninterface AnswerGenerationContext {\n originalQuestion: string;\n statements: SmartSearchStatement[];\n sources: SmartSearchSourcePage[];\n}\n\\`\\`\\`\n\n## Relevance Level Interpretation\n- \\`DIRECT_ANSWER\\`: Prioritize these statements first\n- \\`HIGHLY_RELEVANT\\`: Strong secondary focus\n- \\`SOMEWHAT_RELEVANT\\`: Use for additional context\n- \\`TANGENTIAL\\`: Optional supplementary information\n- \\`NOT_RELEVANT\\`: Ignore completely\n\n## Answer Generation Guidelines\n\n### Content Construction Rules:\n1. Use ONLY information from the provided statements\n2. Prioritize statements with 'DIRECT_ANSWER' and 'HIGHLY_RELEVANT' relevance\n3. Create a comprehensive, informative answer\n4. Maintain scientific accuracy and depth\n\n### Citation Methodology:\n- Place citations IMMEDIATELY after relevant statements\n- Use SQUARE BRACKETS with NUMERIC source IDs\n- Format: \\`Statement of fact.[1][2]\\`\n- Cite EVERY substantive statement\n- Match citations exactly to source IDs\n\n### Structural Requirements:\n1. Detailed Main Answer\n - Comprehensive explanation\n - Technical depth\n - Precise scientific language\n - Full source citations\n\n2. Follow-Up Questions Section\n - Generate 3-4 thought-provoking questions\n - Encourage deeper exploration\n - Based on answer content\n - Formatted as a bulleted list\n\n3. Sources Section\n - List all cited sources\n - Include source titles and URLs\n - Order based on first citation appearance\n\n## Output Example Structure:\n\\`\\`\\`\n[Comprehensive, cited answer with source IDs in brackets]\n\nFollow-up Questions:\n- Question about deeper aspect of the topic\n- Question exploring related concepts\n- Question encouraging further research\n\nSources:\n[1] Source Title (URL)\n[2] Another Source Title (URL)\n...\n\\`\\`\\`\n\n## Critical Constraints:\n- NEVER introduce information not in the statements\n- Preserve exact factual content\n- Ensure grammatical and logical coherence\n- Provide a complete, informative answer\n- Maintain academic rigor\n\n## Processing Instructions:\n- Analyze statements systematically\n- Synthesize information coherently\n- Break down complex concepts\n- Provide scientific context\n- Explain underlying mechanisms\n\n\nThis is the input context:\n${JSON.stringify(context)}\n\n`;\n\nconst searchEngineQueryGenerator = (query: string) => {\n return `\n# Search Query and Source Selection Prompt\n\nYou are an expert at transforming natural language questions into precise search queries and selecting the most appropriate information source.\n\n## Source Selection Guidelines:\n- WIKIPEDIA: Best for general knowledge, scientific explanations, historical information\n- WOLFRAMALPHA: Ideal for mathematical, statistical, computational queries, scientific calculations\n- OTHER: General web search for current events, recent developments, practical information\n\n## Output Requirements:\n- Provide a JSON response with three key fields\n- Do NOT use code block backticks\n- Ensure \"preferred_sources\" is an array\n- Make search query concise and targeted\n\n## Examples:\n\n### Example 1\n- User Query: \"What is the speed of light?\"\n- Output:\n{\n\"origin_question\": \"What is the speed of light?\",\n\"preferred_sources\": [\"WOLFRAMALPHA\"],\n\"search_query\": \"speed of light exact value meters per second\"\n}\n\n### Example 2\n- User Query: \"Who was Marie Curie?\"\n- Output:\n{\n\"origin_question\": \"Who was Marie Curie?\",\n\"preferred_sources\": [\"WIKIPEDIA\"],\n\"search_query\": \"Marie Curie biography scientific achievements\"\n}\n\n### Example 3\n- User Query: \"Best restaurants in New York City\"\n- Output:\n{\n\"origin_question\": \"Best restaurants in New York City\",\n\"preferred_sources\": [\"OTHER\"],\n\"search_query\": \"top rated restaurants NYC 2024 dining\"\n}\n\n### Example 4\n- User Query: \"How do solar panels work?\"\n- Output:\n{\n\"origin_question\": \"How do solar panels work?\",\n\"preferred_sources\": [\"WIKIPEDIA\", \"OTHER\"],\n\"search_query\": \"solar panel photovoltaic technology mechanism\"\n}\n\n## Instructions:\n- Carefully analyze the user's query\n- Select the MOST APPROPRIATE source(s)\n- Create a targeted search query\n- Return ONLY the JSON without additional text\n\nUser Query: ${query}\n`\n\n}\n\nconst statementExtract = (originalQuestion: string, source: SmartSearchSourcePage): string => `\n\n# Fact Extraction Instructions\n\n## Input JSON Structure\n\\`\\`\\`json\n{\n \"originalQuestion\": \"string - The user's original question\",\n \"source\": {\n \"id\": \"number - Unique identifier for the source\",\n \"url\": \"string - URL of the source page\",\n \"title\": \"string - Title of the source page\",\n \"markdown\": \"string - Full text content of the source page\"\n }\n}\n\\`\\`\\`\n\n## Output JSON Structure\n\\`\\`\\`json\n{\n \"sourceId\": \"number - ID of the source\",\n \"sourceTitle\": \"string - Title of the source\",\n \"extractedFacts\": [\n {\n \"statement\": \"string - Verbatim text from the source\",\n \"relevance\": \"string - One of ['DIRECT_ANSWER', 'HIGHLY_RELEVANT', 'SOMEWHAT_RELEVANT', 'TANGENTIAL', 'NOT_RELEVANT']\"\n }\n ]\n}\n\\`\\`\\`\n\n## Relevance Classification Guide:\n- \\`DIRECT_ANSWER\\`: \n - Completely and precisely addresses the original question\n - Contains the core information needed to fully respond\n - Minimal to no additional context required\n\n- \\`HIGHLY_RELEVANT\\`: \n - Provides substantial information directly related to the question\n - Offers critical context or partial solution\n - Significantly contributes to understanding\n\n- \\`SOMEWHAT_RELEVANT\\`: \n - Provides partial or indirect information\n - Offers peripheral insights\n - Requires additional context to be fully meaningful\n\n- \\`TANGENTIAL\\`: \n - Loosely connected to the topic\n - Provides background or related information\n - Not directly addressing the core question\n\n- \\`NOT_RELEVANT\\`: \n - No meaningful connection to the original question\n - Completely unrelated information\n\n\n ## Extraction Guidelines:\n 1. Read the entire source document carefully\n 2. Extract EXACT quotes that:\n - Are actually helpful answering the provided question\n - Are stated verbatim from the source or are rephrased in such a way that doesn't distort the meaning in the original source\n - Represent complete thoughts or meaningful segments\n 3. Classify each extracted fact with its relevance level\n 4. Preserve original context and nuance\n\n## Critical Rules:\n- try NOT to paraphrase or modify the original text\n- Avoid any text in the \"statement\" field that is not helpful answering the provided question like javascript, URLs, HTML, and other non-textual content\n- Extract statements as they appear in the source and ONLY if they are helpful answering the provided question\n- Include full sentences or meaningful text segments\n- Preserve original formatting and punctuation\n- Sort extracted facts by relevance (DIRECT_ANSWER first)\n- Output JSON without \\`\\`\\`json\\`\\`\\` tags, or without any escape characters or any text that is not JSON or my system will crash.\n\n## Processing Instructions:\n- Analyze the entire document systematically\n- Be comprehensive in fact extraction\n- Err on the side of inclusion when in doubt\n- Focus on factual, informative statements\n\n==BEGIN INPUT==\nOriginal Question: ${originalQuestion}\n\nSource:\n${JSON.stringify(source)}\n==END INPUT==\n\n`\nconst debug = []\nfunction tryToExtractJSON(text: string): string {\n const regex = /```(?:json)?\\n([\\s\\S]+?)\\n```/;\n const match = text.match(regex);\n if (match) return match[1];\n else return text;\n}\n\nconst ProcessQuestionError = (step: string, error: Error): string =>\n `Failed to process question at ${step}: ${error.message}`;\n\nasync function conversionToSearchQuery(question: string): Promise {\n const prompt = searchEngineQueryGenerator(question);\n const optimizedQueryResult = await shinkaiLlmPromptProcessor({ format: 'text' , prompt });\n try {\n const result = JSON.parse(optimizedQueryResult.message.trim()) as SearchQueryConversion;\n return result;\n } catch (error) {\n throw new Error(ProcessQuestionError('question processing in optimizequery', new Error(String(error))));\n }\n}\n\n\nasync function extractSourcesFromSearchEngine(\n searchQuery: string,\n engine: SearchEngine,\n apiKey?: string,\n): Promise {\n switch (engine) {\n\t\tcase 'GOOGLE' : {\n\t\t\tconst results = await googleSearch({ query: searchQuery });\n\t\t\treturn results.results;\n\t\t}\n case 'DUCKDUCKGO':\n throw new Error('DuckDuckGo is not supported yet');\n case 'BRAVE': \n throw new Error('Brave is not supported yet');\n default:\n throw new Error('Invalid or unsupperted search engine');\n }\n}\n\nexport async function run(\n config: CONFIG,\n inputs: INPUTS\n): Promise {\n const { question } = inputs;\n if (!question) {\n throw new Error('Question is required in inputs');\n }\n\n try {\n // Step 1: Generate optimized search query\n const searchQuery = await conversionToSearchQuery(question);\n // Step 2: Perform search with optimized query\n const sources: SmartSearchSource[] = []\n for (const preferred_source of searchQuery.preferred_sources) {\n switch (preferred_source) {\n case 'WIKIPEDIA':{\n const searchEngineQuery = searchQuery.search_query+' site:wikipedia.org';\n const searchEngine = config.searchEngine || 'GOOGLE';\n const sourcesSearchResults: SearchResult[] = await extractSourcesFromSearchEngine(searchEngineQuery, searchEngine, config.searchEngineApiKey);\n try {\n const maxSources = config.maxSources ?? 3;\n sources.push(...(sourcesSearchResults.slice(0, Number(maxSources)) as SearchResult[]));\n } catch (error) {\n console.error('Failed to process search results', error);\n throw new Error('Failed to process search results');\n }\n break;\n }\n case 'WOLFRAMALPHA':\n throw new Error('WOLFRAMALPHA is not supported yet');\n case 'OTHER':\n break;\n default:\n throw new Error('Invalid source');\n }\n }\n const smartSearchSouces: SmartSearchSourcePage[] = []\n let id = 1;\n for (const source of sources) {\n if (typeof source === 'string') throw new Error('Invalid source');\n const searchResult = await downloadPages({ url: source.url });\n smartSearchSouces.push({\n id: id++, url: source.url, title: source.title,\n markdown: searchResult.markdown ?? '',\n });\n }\n const statements: SmartSearchStatement[] = []\n // Step 3: Extract statements from sources\n for (const smartSearchSource of smartSearchSouces) {\n const statementString = await shinkaiLlmPromptProcessor({ format: 'text', prompt: statementExtract(question, smartSearchSource) });\n const cleanStatementString = tryToExtractJSON(statementString.message)\n try { \n const statement = JSON.parse(cleanStatementString) as SmartSearchStatement;\n statements.push(statement);\n } catch (error) {\n console.error('Failed to process statement', smartSearchSource.url, error);\n console.error(cleanStatementString)\n }\n }\n // clean markdown from sources for lighter input\n smartSearchSouces.forEach(source => delete source.markdown);\n const generationContext: SmartSearchGenerationContext = {\n originalQuestion: question,\n statements,\n sources: smartSearchSouces,\n }\n // Step 4: Generate answer\n const answerPrompt = answerGenerator(generationContext);\n\t\tconst response = await shinkaiLlmPromptProcessor({ format: 'text', prompt: answerPrompt });\n return {\n statements,\n sources: smartSearchSouces,\n response: response.message,\n };\n } catch (error) {\n throw new Error(ProcessQuestionError('question processing in answer generation', new Error(String(error))));\n }\n}\n","tools":["local:::__official_shinkai:::google_search:::1.0.0","local:::__official_shinkai:::shinkai_llm_prompt_processor:::1.0.0","local:::__official_shinkai:::download_pages:::1.0.0"],"config":[{"BasicConfig":{"key_name":"searchEngine","description":"The search engine to use","required":false,"type":null,"key_value":null}},{"BasicConfig":{"key_name":"searchEngineApiKey","description":"The API key for the search engine","required":false,"type":null,"key_value":null}},{"BasicConfig":{"key_name":"maxSources","description":"The maximum number of sources to return","required":false,"type":null,"key_value":null}}],"description":"This function takes a question as input and returns a comprehensive answer, along with the sources and statements used to generate the answer.","keywords":["search","answer generation","fact extraction","wikipedia","google"],"input_args":{"type":"object","properties":{"question":{"type":"string","description":"The question to answer"}},"required":["question"]},"output_arg":{"json":""},"activated":false,"embedding":[0.44519925,0.011538006,-0.110713586,-0.22075558,-0.14790598,-0.29269773,-0.4754676,0.44221854,-0.121888064,0.39252597,-0.3006528,1.0143435,0.36214498,-0.31270468,0.6776067,0.25015622,0.35587713,-0.23176439,-1.9852347,0.016676497,0.5186351,0.13615943,0.34565818,0.11482762,-0.014134172,0.29590514,0.31907755,-0.3896206,-1.2329955,-1.6192199,0.85829604,0.62480825,-0.6851429,-0.4065035,-0.57009935,-0.44622755,-0.018254763,-0.29652905,0.06502104,0.08794068,0.123337545,-0.2962008,-0.7889936,-0.2309857,-0.30533978,0.00014958903,-0.100521065,-0.17255962,0.65703535,0.59996736,-0.34312758,-0.3323959,-0.2751081,0.054695282,-0.6813821,-0.16973025,-0.34893867,-0.60133564,-0.1950637,-0.30053732,0.61119884,0.13490543,-4.0774403,-0.1405774,0.52335876,0.2547453,0.10967965,-0.37900138,0.33353627,-0.0860852,0.06344949,0.27401373,0.35607475,0.3472401,0.20127875,-0.2749816,-0.1676489,-0.3010119,0.40944278,-0.60488445,-0.33455145,0.036982603,0.1607093,0.18134978,-0.50376195,0.4321913,0.21538696,-0.3986729,0.1924279,-0.07740533,-0.5905008,-0.32293463,-0.33489698,0.08675105,-0.38169703,0.20435761,0.1692553,0.48983234,0.4500325,3.2424073,0.30719528,-0.3322998,0.36166048,-1.097402,0.9221763,-0.3271866,0.15179256,-0.7435132,0.7849906,-0.076523826,0.4214928,-0.20277414,-0.008337051,0.84142566,-0.14813311,0.10049243,-0.48139656,0.091330424,0.08260442,0.73960954,-0.13112147,0.17086662,-0.43677095,-0.3310505,-0.25675213,0.6580711,-0.3097783,0.3686354,0.40853876,0.52439237,0.45975456,-0.5577316,-1.1559865,-0.028603502,0.3412104,0.350788,0.30436534,-0.84187645,0.6342305,-0.7242338,0.30563703,-1.3071945,0.56033385,-0.11213897,0.7491704,-0.037651196,-0.66392916,-0.22443774,-0.40022716,-0.4695392,0.12346482,0.42506516,-0.008828731,-0.036567945,0.7910692,0.4870108,-0.10458979,-0.0010068789,-0.19713187,0.18817124,-0.34421226,0.43161416,0.35337302,0.45155972,0.31935665,-0.38856277,0.14921239,0.46791223,-0.19556057,-0.1643018,0.4380207,-0.46539715,-0.2832293,0.81312156,-0.052545656,-0.29040393,0.1400195,-0.12654832,0.41849804,-0.882607,-0.13826783,0.77245986,-0.5711882,-0.27059942,-0.19140518,0.4529812,0.0826526,-0.367387,0.64419246,1.1117058,-0.47169876,1.4447793,-0.2390542,-0.19558607,0.1427455,-0.21482131,0.104871795,0.2707702,0.38783553,0.1292122,-1.0809127,-0.0846922,-0.039507207,-0.3097255,-0.29537687,-0.65163094,0.19342569,-0.32778674,-0.08130689,-0.64830554,0.12468888,-0.5193145,1.1083255,-0.010225475,0.6004076,-0.14024194,0.058595452,0.4302386,-0.47035223,0.8164973,0.21753985,-0.013964482,-0.6944066,-0.62527126,-0.82455045,-0.112839624,0.14301777,0.12782156,-0.2723977,0.008159652,0.5853714,0.9211197,0.4646956,1.390659,0.76697886,0.523064,-0.12873787,0.25171667,0.17022641,-0.30911857,0.31879586,-0.1103535,-0.33922687,-0.32489824,0.3671196,-0.54260576,-0.010865424,-0.16758339,0.06505124,1.4560661,0.7549216,0.18457569,-0.04554111,0.9045233,-0.14435571,0.16820474,-1.6039296,0.078886904,-0.30494863,0.45922333,-0.011344843,-0.3130207,0.39990506,0.3539346,-0.6010364,-0.8332309,-0.37220326,-0.80922806,-0.17530465,-0.06533443,-0.109381296,0.35899848,-0.47536635,-0.042883083,0.5028103,0.15244354,0.3528542,0.5739549,-0.58191675,-0.13802925,0.4039406,0.15418546,0.10369478,0.24870338,-0.14724898,-0.1899396,-0.4523422,-0.067849,-0.45093012,0.7759565,-0.47772875,-0.5009712,-0.63364625,0.32183337,1.6104475,0.31228143,-0.2737522,0.8513892,-0.10785564,-0.083213486,0.3272493,0.34409443,-0.102422476,0.14750528,-0.21719596,-0.7646839,0.7050644,-0.43036377,-0.34817296,-0.07673599,-0.8938274,-0.259611,0.085467875,-0.35622948,0.17879769,-0.4371638,0.84722894,0.42178243,0.47265047,-2.0367239,-0.28862223,0.2279939,0.023028731,0.03350927,-0.84622985,0.6191283,-0.36584458,0.52736944,-0.8522264,1.7012173,0.623458,-0.34564644,-0.29220566,0.22257191,1.008989,-0.5586227,-0.100436315,-0.20206258,-0.14138165,0.29932487,0.3574607,1.777014,0.44976053,0.1803996,0.17181638,0.11031827,-0.18544856,-1.3960699,0.18656585,0.43533033,-0.32739973,0.56046206,-0.08367232,-0.45680672,0.45554873,1.0323718,0.2787497,0.19714256,-0.0057662483,2.0173283,0.14193904,-0.33267602,-0.4824432,-0.44496542,-0.44327742,0.33923206,0.24754228,-0.22856423,0.23647808,-0.4234256,-0.11778459,-0.19414693,0.28126818,-0.0637252,0.28323674,0.4969948,-0.15884419,0.684458,0.3888534,-0.17290172,0.74648315,0.03662668,-1.0852582,-0.24843164],"result":{"type":"object","properties":{"response":{"description":"The generated answer","type":"string"},"sources":{"description":"The sources used to generate the answer","items":{"properties":{"id":{"type":"number"},"title":{"type":"string"},"url":{"type":"string"}},"type":"object"},"type":"array"},"statements":{"description":"The statements extracted from the sources","items":{"properties":{"extractedFacts":{"items":{"properties":{"relevance":{"type":"string"},"statement":{"type":"string"}},"type":"object"},"type":"array"},"sourceId":{"type":"number"},"sourceTitle":{"type":"string"}},"type":"object"},"type":"array"}},"required":["response","sources","statements"]},"sql_tables":[],"sql_queries":[],"file_inbox":null,"oauth":null,"assets":null,"runner":"any","operating_system":["linux","macos","windows"],"tool_set":""},false]} \ No newline at end of file +{"type":"Deno","content":[{"name":"Smart Search Engine","homepage":null,"author":"@@official.shinkai","version":"1.0.0","js_code":"import { googleSearch, shinkaiLlmPromptProcessor, downloadPages } from './shinkai-local-tools.ts';\n\ntype CONFIG = {\n searchEngineApiKey?: string;\n searchEngine?: SearchEngine;\n maxSources?: number;\n}\ntype INPUTS = {\n question: string;\n};\ntype OUTPUT = {\n response: string;\n sources: SmartSearchSourcePage[];\n statements: SmartSearchStatement[];\n}\ntype PREFFERED_SOURCES = 'WIKIPEDIA'|'WOLFRAMALPHA'|'OTHER';\n\ntype SearchQueryConversion = {\n \"origin_question\": string;\n \"preferred_sources\": PREFFERED_SOURCES[];\n \"search_query\": string\n}\n\ntype SearchResult = {\n title: string;\n description: string;\n url: string;\n}\n\ntype SmartSearchSource = SearchResult | string;\ntype SearchEngine = 'DUCKDUCKGO' | 'GOOGLE' | 'BRAVE';\n\nexport interface SmartSearchSourcePage {\n id: number;\n url: string;\n markdown?: string;\n title: string;\n}\n\nexport interface SmartSearchStatement {\n sourceId: number;\n sourceTitle: string;\n extractedFacts: {\n statement: string;\n relevance: 'DIRECT_ANSWER' | 'HIGHLY_RELEVANT' | 'SOMEWHAT_RELEVANT' | 'TANGENTIAL' | 'NOT_RELEVANT';\n }[];\n}\nexport interface SmartSearchGenerationContext {\n originalQuestion: string;\n statements: SmartSearchStatement[];\n sources: SmartSearchSourcePage[];\n}\n\nconst answerGenerator = (context: SmartSearchGenerationContext): string => `\n# Smart Search Answer Generation Instructions\nYou are a sophisticated scientific communication assistant specialized in transforming extracted research statements into comprehensive, accessible, and precisely cited explanations.Your primary objective is to synthesize complex information from multiple sources into a clear, authoritative answer that maintains absolute fidelity to the source material. Think of yourself as an academic translator - your role is to take fragmented scientific statements and weave them into a coherent narrative that is both intellectually rigorous and engaging, ensuring that every substantive claim is meticulously attributed to its original source. Approach each question as an opportunity to provide a deep, nuanced understanding that goes beyond surface-level explanation, while maintaining strict scholarly integrity.\n## Input JSON Interfaces and Definitions\n\n\\`\\`\\`typescript\n// Source Page Interface\nexport interface SmartSearchSourcePage {\n id: number; // Unique identifier for the source\n url: string; // Full URL of the source\n markdown: string; // Full text content of the source page\n title: string; // Title of the source page\n}\n\n// Statement Interface with Detailed Relevance Levels\nexport interface SmartSearchStatement {\n sourceId: number; // ID of the source this statement comes from\n sourceTitle: string; // Title of the source\n extractedFacts: {\n statement: string; // Exact verbatim text from the source\n relevance: 'DIRECT_ANSWER' \n | 'HIGHLY_RELEVANT' \n | 'SOMEWHAT_RELEVANT' \n | 'TANGENTIAL' \n | 'NOT_RELEVANT'; // Relevance classification\n }[];\n}\n\n// Complete Input JSON Structure\ninterface AnswerGenerationContext {\n originalQuestion: string;\n statements: SmartSearchStatement[];\n sources: SmartSearchSourcePage[];\n}\n\\`\\`\\`\n\n## Relevance Level Interpretation\n- \\`DIRECT_ANSWER\\`: Prioritize these statements first\n- \\`HIGHLY_RELEVANT\\`: Strong secondary focus\n- \\`SOMEWHAT_RELEVANT\\`: Use for additional context\n- \\`TANGENTIAL\\`: Optional supplementary information\n- \\`NOT_RELEVANT\\`: Ignore completely\n\n## Answer Generation Guidelines\n\n### Content Construction Rules:\n1. Use ONLY information from the provided statements\n2. Prioritize statements with 'DIRECT_ANSWER' and 'HIGHLY_RELEVANT' relevance\n3. Create a comprehensive, informative answer\n4. Maintain scientific accuracy and depth\n\n### Citation Methodology:\n- Place citations IMMEDIATELY after relevant statements\n- Use SQUARE BRACKETS with NUMERIC source IDs\n- Format: \\`Statement of fact.[1][2]\\`\n- Cite EVERY substantive statement\n- Match citations exactly to source IDs\n\n### Structural Requirements:\n1. Detailed Main Answer\n - Comprehensive explanation\n - Technical depth\n - Precise scientific language\n - Full source citations\n\n2. Follow-Up Questions Section\n - Generate 3-4 thought-provoking questions\n - Encourage deeper exploration\n - Based on answer content\n - Formatted as a bulleted list\n\n3. Sources Section\n - List all cited sources\n - Include source titles and URLs\n - Order based on first citation appearance\n\n## Output Example Structure:\n\\`\\`\\`\n[Comprehensive, cited answer with source IDs in brackets]\n\nFollow-up Questions:\n- Question about deeper aspect of the topic\n- Question exploring related concepts\n- Question encouraging further research\n\nSources:\n[1] Source Title (URL)\n[2] Another Source Title (URL)\n...\n\\`\\`\\`\n\n## Critical Constraints:\n- NEVER introduce information not in the statements\n- Preserve exact factual content\n- Ensure grammatical and logical coherence\n- Provide a complete, informative answer\n- Maintain academic rigor\n\n## Processing Instructions:\n- Analyze statements systematically\n- Synthesize information coherently\n- Break down complex concepts\n- Provide scientific context\n- Explain underlying mechanisms\n\n\nThis is the input context:\n${JSON.stringify(context)}\n\n`;\n\nconst searchEngineQueryGenerator = (query: string) => {\n return `\n# Search Query and Source Selection Prompt\n\nYou are an expert at transforming natural language questions into precise search queries and selecting the most appropriate information source.\n\n## Source Selection Guidelines:\n- WIKIPEDIA: Best for general knowledge, scientific explanations, historical information\n- WOLFRAMALPHA: Ideal for mathematical, statistical, computational queries, scientific calculations\n- OTHER: General web search for current events, recent developments, practical information\n\n## Output Requirements:\n- Provide a JSON response with three key fields\n- Do NOT use code block backticks\n- Ensure \"preferred_sources\" is an array\n- Make search query concise and targeted\n\n## Examples:\n\n### Example 1\n- User Query: \"What is the speed of light?\"\n- Output:\n{\n\"origin_question\": \"What is the speed of light?\",\n\"preferred_sources\": [\"WOLFRAMALPHA\"],\n\"search_query\": \"speed of light exact value meters per second\"\n}\n\n### Example 2\n- User Query: \"Who was Marie Curie?\"\n- Output:\n{\n\"origin_question\": \"Who was Marie Curie?\",\n\"preferred_sources\": [\"WIKIPEDIA\"],\n\"search_query\": \"Marie Curie biography scientific achievements\"\n}\n\n### Example 3\n- User Query: \"Best restaurants in New York City\"\n- Output:\n{\n\"origin_question\": \"Best restaurants in New York City\",\n\"preferred_sources\": [\"OTHER\"],\n\"search_query\": \"top rated restaurants NYC 2024 dining\"\n}\n\n### Example 4\n- User Query: \"How do solar panels work?\"\n- Output:\n{\n\"origin_question\": \"How do solar panels work?\",\n\"preferred_sources\": [\"WIKIPEDIA\", \"OTHER\"],\n\"search_query\": \"solar panel photovoltaic technology mechanism\"\n}\n\n## Instructions:\n- Carefully analyze the user's query\n- Select the MOST APPROPRIATE source(s)\n- Create a targeted search query\n- Return ONLY the JSON without additional text\n\nUser Query: ${query}\n`\n\n}\n\nconst statementExtract = (originalQuestion: string, source: SmartSearchSourcePage): string => `\n\n# Fact Extraction Instructions\n\n## Input JSON Structure\n\\`\\`\\`json\n{\n \"originalQuestion\": \"string - The user's original question\",\n \"source\": {\n \"id\": \"number - Unique identifier for the source\",\n \"url\": \"string - URL of the source page\",\n \"title\": \"string - Title of the source page\",\n \"markdown\": \"string - Full text content of the source page\"\n }\n}\n\\`\\`\\`\n\n## Output JSON Structure\n\\`\\`\\`json\n{\n \"sourceId\": \"number - ID of the source\",\n \"sourceTitle\": \"string - Title of the source\",\n \"extractedFacts\": [\n {\n \"statement\": \"string - Verbatim text from the source\",\n \"relevance\": \"string - One of ['DIRECT_ANSWER', 'HIGHLY_RELEVANT', 'SOMEWHAT_RELEVANT', 'TANGENTIAL', 'NOT_RELEVANT']\"\n }\n ]\n}\n\\`\\`\\`\n\n## Relevance Classification Guide:\n- \\`DIRECT_ANSWER\\`: \n - Completely and precisely addresses the original question\n - Contains the core information needed to fully respond\n - Minimal to no additional context required\n\n- \\`HIGHLY_RELEVANT\\`: \n - Provides substantial information directly related to the question\n - Offers critical context or partial solution\n - Significantly contributes to understanding\n\n- \\`SOMEWHAT_RELEVANT\\`: \n - Provides partial or indirect information\n - Offers peripheral insights\n - Requires additional context to be fully meaningful\n\n- \\`TANGENTIAL\\`: \n - Loosely connected to the topic\n - Provides background or related information\n - Not directly addressing the core question\n\n- \\`NOT_RELEVANT\\`: \n - No meaningful connection to the original question\n - Completely unrelated information\n\n\n ## Extraction Guidelines:\n 1. Read the entire source document carefully\n 2. Extract EXACT quotes that:\n - Are actually helpful answering the provided question\n - Are stated verbatim from the source or are rephrased in such a way that doesn't distort the meaning in the original source\n - Represent complete thoughts or meaningful segments\n 3. Classify each extracted fact with its relevance level\n 4. Preserve original context and nuance\n\n## Critical Rules:\n- try NOT to paraphrase or modify the original text\n- Avoid any text in the \"statement\" field that is not helpful answering the provided question like javascript, URLs, HTML, and other non-textual content\n- Extract statements as they appear in the source and ONLY if they are helpful answering the provided question\n- Include full sentences or meaningful text segments\n- Preserve original formatting and punctuation\n- Sort extracted facts by relevance (DIRECT_ANSWER first)\n- Output JSON without \\`\\`\\`json\\`\\`\\` tags, or without any escape characters or any text that is not JSON or my system will crash.\n\n## Processing Instructions:\n- Analyze the entire document systematically\n- Be comprehensive in fact extraction\n- Err on the side of inclusion when in doubt\n- Focus on factual, informative statements\n\n==BEGIN INPUT==\nOriginal Question: ${originalQuestion}\n\nSource:\n${JSON.stringify(source)}\n==END INPUT==\n\n`\nconst debug = []\nfunction tryToExtractJSON(text: string): string {\n const regex = /```(?:json)?\\n([\\s\\S]+?)\\n```/;\n const match = text.match(regex);\n if (match) return match[1];\n else return text;\n}\n\nconst ProcessQuestionError = (step: string, error: Error): string =>\n `Failed to process question at ${step}: ${error.message}`;\n\nasync function conversionToSearchQuery(question: string): Promise {\n const prompt = searchEngineQueryGenerator(question);\n const optimizedQueryResult = await shinkaiLlmPromptProcessor({ format: 'text' , prompt });\n try {\n const result = JSON.parse(optimizedQueryResult.message.trim()) as SearchQueryConversion;\n return result;\n } catch (error) {\n throw new Error(ProcessQuestionError('question processing in optimizequery', new Error(String(error))));\n }\n}\n\n\nasync function extractSourcesFromSearchEngine(\n searchQuery: string,\n engine: SearchEngine,\n apiKey?: string,\n): Promise {\n switch (engine) {\n\t\tcase 'GOOGLE' : {\n\t\t\tconst results = await googleSearch({ query: searchQuery });\n\t\t\treturn results.results;\n\t\t}\n case 'DUCKDUCKGO':\n throw new Error('DuckDuckGo is not supported yet');\n case 'BRAVE': \n throw new Error('Brave is not supported yet');\n default:\n throw new Error('Invalid or unsupperted search engine');\n }\n}\n\nexport async function run(\n config: CONFIG,\n inputs: INPUTS\n): Promise {\n const { question } = inputs;\n if (!question) {\n throw new Error('Question is required in inputs');\n }\n\n try {\n // Step 1: Generate optimized search query\n const searchQuery = await conversionToSearchQuery(question);\n // Step 2: Perform search with optimized query\n const sources: SmartSearchSource[] = []\n for (const preferred_source of searchQuery.preferred_sources) {\n switch (preferred_source) {\n case 'WIKIPEDIA':{\n const searchEngineQuery = searchQuery.search_query+' site:wikipedia.org';\n const searchEngine = config.searchEngine || 'GOOGLE';\n const sourcesSearchResults: SearchResult[] = await extractSourcesFromSearchEngine(searchEngineQuery, searchEngine, config.searchEngineApiKey);\n try {\n const maxSources = config.maxSources ?? 3;\n sources.push(...(sourcesSearchResults.slice(0, Number(maxSources)) as SearchResult[]));\n } catch (error) {\n console.error('Failed to process search results', error);\n throw new Error('Failed to process search results');\n }\n break;\n }\n case 'WOLFRAMALPHA':\n throw new Error('WOLFRAMALPHA is not supported yet');\n case 'OTHER':\n break;\n default:\n throw new Error('Invalid source');\n }\n }\n const smartSearchSouces: SmartSearchSourcePage[] = []\n let id = 1;\n for (const source of sources) {\n if (typeof source === 'string') throw new Error('Invalid source');\n const searchResult = await downloadPages({ url: source.url });\n smartSearchSouces.push({\n id: id++, url: source.url, title: source.title,\n markdown: searchResult.markdown ?? '',\n });\n }\n const statements: SmartSearchStatement[] = []\n // Step 3: Extract statements from sources\n for (const smartSearchSource of smartSearchSouces) {\n const statementString = await shinkaiLlmPromptProcessor({ format: 'text', prompt: statementExtract(question, smartSearchSource) });\n const cleanStatementString = tryToExtractJSON(statementString.message)\n try { \n const statement = JSON.parse(cleanStatementString) as SmartSearchStatement;\n statements.push(statement);\n } catch (error) {\n console.error('Failed to process statement', smartSearchSource.url, error);\n console.error(cleanStatementString)\n }\n }\n // clean markdown from sources for lighter input\n smartSearchSouces.forEach(source => delete source.markdown);\n const generationContext: SmartSearchGenerationContext = {\n originalQuestion: question,\n statements,\n sources: smartSearchSouces,\n }\n // Step 4: Generate answer\n const answerPrompt = answerGenerator(generationContext);\n\t\tconst response = await shinkaiLlmPromptProcessor({ format: 'text', prompt: answerPrompt });\n return {\n statements,\n sources: smartSearchSouces,\n response: response.message,\n };\n } catch (error) {\n throw new Error(ProcessQuestionError('question processing in answer generation', new Error(String(error))));\n }\n}\n","tools":["local:::__official_shinkai:::google_search:::1.0.0","local:::__official_shinkai:::shinkai_llm_prompt_processor:::1.0.0","local:::__official_shinkai:::download_pages:::1.0.0"],"config":[{"BasicConfig":{"key_name":"searchEngine","description":"The search engine to use","required":false,"type":null,"key_value":null}},{"BasicConfig":{"key_name":"searchEngineApiKey","description":"The API key for the search engine","required":false,"type":null,"key_value":null}},{"BasicConfig":{"key_name":"maxSources","description":"The maximum number of sources to return","required":false,"type":null,"key_value":null}}],"description":"This function takes a question as input and returns a comprehensive answer, along with the sources and statements used to generate the answer.","keywords":["search","answer generation","fact extraction","wikipedia","google"],"input_args":{"type":"object","properties":{"question":{"type":"string","description":"The question to answer"}},"required":["question"]},"output_arg":{"json":""},"activated":false,"embedding":[0.44519925,0.011538006,-0.110713586,-0.22075558,-0.14790598,-0.29269773,-0.4754676,0.44221854,-0.121888064,0.39252597,-0.3006528,1.0143435,0.36214498,-0.31270468,0.6776067,0.25015622,0.35587713,-0.23176439,-1.9852347,0.016676497,0.5186351,0.13615943,0.34565818,0.11482762,-0.014134172,0.29590514,0.31907755,-0.3896206,-1.2329955,-1.6192199,0.85829604,0.62480825,-0.6851429,-0.4065035,-0.57009935,-0.44622755,-0.018254763,-0.29652905,0.06502104,0.08794068,0.123337545,-0.2962008,-0.7889936,-0.2309857,-0.30533978,0.00014958903,-0.100521065,-0.17255962,0.65703535,0.59996736,-0.34312758,-0.3323959,-0.2751081,0.054695282,-0.6813821,-0.16973025,-0.34893867,-0.60133564,-0.1950637,-0.30053732,0.61119884,0.13490543,-4.0774403,-0.1405774,0.52335876,0.2547453,0.10967965,-0.37900138,0.33353627,-0.0860852,0.06344949,0.27401373,0.35607475,0.3472401,0.20127875,-0.2749816,-0.1676489,-0.3010119,0.40944278,-0.60488445,-0.33455145,0.036982603,0.1607093,0.18134978,-0.50376195,0.4321913,0.21538696,-0.3986729,0.1924279,-0.07740533,-0.5905008,-0.32293463,-0.33489698,0.08675105,-0.38169703,0.20435761,0.1692553,0.48983234,0.4500325,3.2424073,0.30719528,-0.3322998,0.36166048,-1.097402,0.9221763,-0.3271866,0.15179256,-0.7435132,0.7849906,-0.076523826,0.4214928,-0.20277414,-0.008337051,0.84142566,-0.14813311,0.10049243,-0.48139656,0.091330424,0.08260442,0.73960954,-0.13112147,0.17086662,-0.43677095,-0.3310505,-0.25675213,0.6580711,-0.3097783,0.3686354,0.40853876,0.52439237,0.45975456,-0.5577316,-1.1559865,-0.028603502,0.3412104,0.350788,0.30436534,-0.84187645,0.6342305,-0.7242338,0.30563703,-1.3071945,0.56033385,-0.11213897,0.7491704,-0.037651196,-0.66392916,-0.22443774,-0.40022716,-0.4695392,0.12346482,0.42506516,-0.008828731,-0.036567945,0.7910692,0.4870108,-0.10458979,-0.0010068789,-0.19713187,0.18817124,-0.34421226,0.43161416,0.35337302,0.45155972,0.31935665,-0.38856277,0.14921239,0.46791223,-0.19556057,-0.1643018,0.4380207,-0.46539715,-0.2832293,0.81312156,-0.052545656,-0.29040393,0.1400195,-0.12654832,0.41849804,-0.882607,-0.13826783,0.77245986,-0.5711882,-0.27059942,-0.19140518,0.4529812,0.0826526,-0.367387,0.64419246,1.1117058,-0.47169876,1.4447793,-0.2390542,-0.19558607,0.1427455,-0.21482131,0.104871795,0.2707702,0.38783553,0.1292122,-1.0809127,-0.0846922,-0.039507207,-0.3097255,-0.29537687,-0.65163094,0.19342569,-0.32778674,-0.08130689,-0.64830554,0.12468888,-0.5193145,1.1083255,-0.010225475,0.6004076,-0.14024194,0.058595452,0.4302386,-0.47035223,0.8164973,0.21753985,-0.013964482,-0.6944066,-0.62527126,-0.82455045,-0.112839624,0.14301777,0.12782156,-0.2723977,0.008159652,0.5853714,0.9211197,0.4646956,1.390659,0.76697886,0.523064,-0.12873787,0.25171667,0.17022641,-0.30911857,0.31879586,-0.1103535,-0.33922687,-0.32489824,0.3671196,-0.54260576,-0.010865424,-0.16758339,0.06505124,1.4560661,0.7549216,0.18457569,-0.04554111,0.9045233,-0.14435571,0.16820474,-1.6039296,0.078886904,-0.30494863,0.45922333,-0.011344843,-0.3130207,0.39990506,0.3539346,-0.6010364,-0.8332309,-0.37220326,-0.80922806,-0.17530465,-0.06533443,-0.109381296,0.35899848,-0.47536635,-0.042883083,0.5028103,0.15244354,0.3528542,0.5739549,-0.58191675,-0.13802925,0.4039406,0.15418546,0.10369478,0.24870338,-0.14724898,-0.1899396,-0.4523422,-0.067849,-0.45093012,0.7759565,-0.47772875,-0.5009712,-0.63364625,0.32183337,1.6104475,0.31228143,-0.2737522,0.8513892,-0.10785564,-0.083213486,0.3272493,0.34409443,-0.102422476,0.14750528,-0.21719596,-0.7646839,0.7050644,-0.43036377,-0.34817296,-0.07673599,-0.8938274,-0.259611,0.085467875,-0.35622948,0.17879769,-0.4371638,0.84722894,0.42178243,0.47265047,-2.0367239,-0.28862223,0.2279939,0.023028731,0.03350927,-0.84622985,0.6191283,-0.36584458,0.52736944,-0.8522264,1.7012173,0.623458,-0.34564644,-0.29220566,0.22257191,1.008989,-0.5586227,-0.100436315,-0.20206258,-0.14138165,0.29932487,0.3574607,1.777014,0.44976053,0.1803996,0.17181638,0.11031827,-0.18544856,-1.3960699,0.18656585,0.43533033,-0.32739973,0.56046206,-0.08367232,-0.45680672,0.45554873,1.0323718,0.2787497,0.19714256,-0.0057662483,2.0173283,0.14193904,-0.33267602,-0.4824432,-0.44496542,-0.44327742,0.33923206,0.24754228,-0.22856423,0.23647808,-0.4234256,-0.11778459,-0.19414693,0.28126818,-0.0637252,0.28323674,0.4969948,-0.15884419,0.684458,0.3888534,-0.17290172,0.74648315,0.03662668,-1.0852582,-0.24843164],"result":{"type":"object","properties":{"response":{"description":"The generated answer","type":"string"},"sources":{"description":"The sources used to generate the answer","items":{"properties":{"id":{"type":"number"},"title":{"type":"string"},"url":{"type":"string"}},"type":"object"},"type":"array"},"statements":{"description":"The statements extracted from the sources","items":{"properties":{"extractedFacts":{"items":{"properties":{"relevance":{"type":"string"},"statement":{"type":"string"}},"type":"object"},"type":"array"},"sourceId":{"type":"number"},"sourceTitle":{"type":"string"}},"type":"object"},"type":"array"}},"required":["response","sources","statements"]},"sql_tables":[],"sql_queries":[],"file_inbox":null,"oauth":null,"assets":null,"runner":"any","operating_system":["linux","macos","windows"],"tool_set":""},false]}