@aj-archipelago/cortex 1.3.35 → 1.3.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/config/default.example.json +0 -20
- package/config.js +160 -6
- package/lib/pathwayTools.js +79 -1
- package/lib/requestExecutor.js +3 -1
- package/lib/util.js +7 -0
- package/package.json +1 -1
- package/pathways/basePathway.js +2 -0
- package/pathways/call_tools.js +379 -0
- package/pathways/system/entity/memory/shared/sys_memory_helpers.js +1 -1
- package/pathways/system/entity/memory/sys_search_memory.js +2 -2
- package/pathways/system/entity/sys_entity_agent.js +289 -0
- package/pathways/system/entity/sys_generator_memory.js +1 -1
- package/pathways/system/entity/sys_generator_results.js +1 -1
- package/pathways/system/entity/sys_get_entities.js +19 -0
- package/pathways/system/entity/tools/shared/sys_entity_tools.js +150 -0
- package/pathways/system/entity/tools/sys_tool_bing_search.js +147 -0
- package/pathways/system/entity/tools/sys_tool_callmodel.js +62 -0
- package/pathways/system/entity/tools/sys_tool_coding.js +53 -0
- package/pathways/system/entity/tools/sys_tool_codingagent.js +100 -0
- package/pathways/system/entity/tools/sys_tool_cognitive_search.js +231 -0
- package/pathways/system/entity/tools/sys_tool_image.js +57 -0
- package/pathways/system/entity/tools/sys_tool_readfile.js +119 -0
- package/pathways/system/entity/tools/sys_tool_reasoning.js +75 -0
- package/pathways/system/entity/tools/sys_tool_remember.js +59 -0
- package/pathways/vision.js +1 -1
- package/server/modelExecutor.js +4 -12
- package/server/pathwayResolver.js +53 -40
- package/server/plugins/azureBingPlugin.js +42 -4
- package/server/plugins/azureCognitivePlugin.js +40 -12
- package/server/plugins/claude3VertexPlugin.js +67 -18
- package/server/plugins/modelPlugin.js +3 -2
- package/server/plugins/openAiReasoningPlugin.js +3 -3
- package/server/plugins/openAiReasoningVisionPlugin.js +48 -0
- package/server/plugins/openAiVisionPlugin.js +192 -7
- package/tests/agentic.test.js +256 -0
- package/tests/call_tools.test.js +216 -0
- package/tests/claude3VertexToolConversion.test.js +78 -0
- package/tests/mocks.js +11 -3
- package/tests/multimodal_conversion.test.js +1 -1
- package/tests/openAiToolPlugin.test.js +242 -0
- package/pathways/test_palm_chat.js +0 -31
- package/server/plugins/palmChatPlugin.js +0 -233
- package/server/plugins/palmCodeCompletionPlugin.js +0 -45
- package/server/plugins/palmCompletionPlugin.js +0 -135
- package/tests/palmChatPlugin.test.js +0 -219
- package/tests/palmCompletionPlugin.test.js +0 -58
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// sys_entity_agent.js
|
|
2
|
+
// Agentic extension of the entity system that uses OpenAI's tool calling API
|
|
3
|
+
import { callPathway, callTool, say } from '../../../lib/pathwayTools.js';
|
|
4
|
+
import logger from '../../../lib/logger.js';
|
|
5
|
+
import { config } from '../../../config.js';
|
|
6
|
+
import { chatArgsHasImageUrl, removeOldImageAndFileContent } from '../../../lib/util.js';
|
|
7
|
+
import { insertToolCallAndResults } from './memory/shared/sys_memory_helpers.js';
|
|
8
|
+
import { Prompt } from '../../../server/prompt.js';
|
|
9
|
+
import { getToolsForEntity, loadEntityConfig } from './tools/shared/sys_entity_tools.js';
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
useInputChunking: false,
|
|
13
|
+
enableDuplicateRequests: false,
|
|
14
|
+
useSingleTokenStream: false,
|
|
15
|
+
inputParameters: {
|
|
16
|
+
privateData: false,
|
|
17
|
+
chatHistory: [{role: '', content: []}],
|
|
18
|
+
contextId: ``,
|
|
19
|
+
indexName: ``,
|
|
20
|
+
semanticConfiguration: ``,
|
|
21
|
+
roleInformation: ``,
|
|
22
|
+
calculateEmbeddings: false,
|
|
23
|
+
dataSources: ["mydata", "aja", "aje", "wires", "bing"],
|
|
24
|
+
language: "English",
|
|
25
|
+
aiName: "Jarvis",
|
|
26
|
+
aiMemorySelfModify: true,
|
|
27
|
+
aiStyle: "OpenAI",
|
|
28
|
+
title: ``,
|
|
29
|
+
messages: [],
|
|
30
|
+
voiceResponse: false,
|
|
31
|
+
codeRequestId: ``,
|
|
32
|
+
skipCallbackMessage: false,
|
|
33
|
+
entityId: ``,
|
|
34
|
+
model: 'oai-gpt41'
|
|
35
|
+
},
|
|
36
|
+
timeout: 600,
|
|
37
|
+
|
|
38
|
+
toolCallback: async (args, message, resolver) => {
|
|
39
|
+
const { tool_calls } = message;
|
|
40
|
+
const pathwayResolver = resolver;
|
|
41
|
+
const { entityTools, entityToolsOpenAiFormat } = args;
|
|
42
|
+
|
|
43
|
+
// Make a deep copy of the initial chat history
|
|
44
|
+
const initialMessages = JSON.parse(JSON.stringify(args.chatHistory || []));
|
|
45
|
+
|
|
46
|
+
if (tool_calls) {
|
|
47
|
+
// Execute tool calls in parallel but with isolated message histories
|
|
48
|
+
const toolResults = await Promise.all(tool_calls.map(async (toolCall) => {
|
|
49
|
+
try {
|
|
50
|
+
if (!toolCall?.function?.arguments) {
|
|
51
|
+
throw new Error('Invalid tool call structure: missing function arguments');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const toolArgs = JSON.parse(toolCall.function.arguments);
|
|
55
|
+
const toolFunction = toolCall.function.name.toLowerCase();
|
|
56
|
+
|
|
57
|
+
// Create an isolated copy of messages for this tool
|
|
58
|
+
const toolMessages = JSON.parse(JSON.stringify(initialMessages));
|
|
59
|
+
|
|
60
|
+
// Get the tool definition to check for icon
|
|
61
|
+
const toolDefinition = entityTools[toolFunction]?.definition;
|
|
62
|
+
const toolIcon = toolDefinition?.icon || '🛠️';
|
|
63
|
+
|
|
64
|
+
// Report status to the user
|
|
65
|
+
const toolUserMessage = toolArgs.userMessage || `Executing tool: ${toolCall.function.name} - ${JSON.stringify(toolArgs)}`;
|
|
66
|
+
const messageWithIcon = toolIcon ? `${toolIcon} ${toolUserMessage}` : toolUserMessage;
|
|
67
|
+
await say(pathwayResolver.rootRequestId || pathwayResolver.requestId, `${messageWithIcon}\n\n`, 1000, false);
|
|
68
|
+
|
|
69
|
+
if (toolArgs.detailedInstructions) {
|
|
70
|
+
toolMessages.push({role: "user", content: toolArgs.detailedInstructions});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Add the tool call to the isolated message history
|
|
74
|
+
toolMessages.push({
|
|
75
|
+
role: "assistant",
|
|
76
|
+
content: "",
|
|
77
|
+
tool_calls: [{
|
|
78
|
+
id: toolCall.id,
|
|
79
|
+
type: "function",
|
|
80
|
+
function: {
|
|
81
|
+
name: toolCall.function.name,
|
|
82
|
+
arguments: JSON.stringify(toolArgs)
|
|
83
|
+
}
|
|
84
|
+
}]
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const toolResult = await callTool(toolFunction, {
|
|
88
|
+
...args,
|
|
89
|
+
...toolArgs,
|
|
90
|
+
toolFunction,
|
|
91
|
+
chatHistory: toolMessages,
|
|
92
|
+
stream: false
|
|
93
|
+
}, entityTools, pathwayResolver);
|
|
94
|
+
|
|
95
|
+
// Add the tool result to the isolated message history
|
|
96
|
+
let toolResultContent = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult);
|
|
97
|
+
|
|
98
|
+
toolMessages.push({
|
|
99
|
+
role: "tool",
|
|
100
|
+
tool_call_id: toolCall.id,
|
|
101
|
+
name: toolCall.function.name,
|
|
102
|
+
content: toolResultContent
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
success: true,
|
|
107
|
+
result: toolResult,
|
|
108
|
+
toolCall,
|
|
109
|
+
toolArgs,
|
|
110
|
+
toolFunction,
|
|
111
|
+
messages: toolMessages
|
|
112
|
+
};
|
|
113
|
+
} catch (error) {
|
|
114
|
+
logger.error(`Error executing tool ${toolCall?.function?.name || 'unknown'}: ${error.message}`);
|
|
115
|
+
|
|
116
|
+
// Create error message history
|
|
117
|
+
const errorMessages = JSON.parse(JSON.stringify(initialMessages));
|
|
118
|
+
errorMessages.push({
|
|
119
|
+
role: "assistant",
|
|
120
|
+
content: "",
|
|
121
|
+
tool_calls: [{
|
|
122
|
+
id: toolCall.id,
|
|
123
|
+
type: "function",
|
|
124
|
+
function: {
|
|
125
|
+
name: toolCall.function.name,
|
|
126
|
+
arguments: JSON.stringify(toolCall.function.arguments)
|
|
127
|
+
}
|
|
128
|
+
}]
|
|
129
|
+
});
|
|
130
|
+
errorMessages.push({
|
|
131
|
+
role: "tool",
|
|
132
|
+
tool_call_id: toolCall.id,
|
|
133
|
+
name: toolCall.function.name,
|
|
134
|
+
content: `Error: ${error.message}`
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
success: false,
|
|
139
|
+
error: error.message,
|
|
140
|
+
toolCall,
|
|
141
|
+
toolArgs: toolCall?.function?.arguments ? JSON.parse(toolCall.function.arguments) : {},
|
|
142
|
+
toolFunction: toolCall?.function?.name?.toLowerCase() || 'unknown',
|
|
143
|
+
messages: errorMessages
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}));
|
|
147
|
+
|
|
148
|
+
// Merge all message histories in order
|
|
149
|
+
let finalMessages = JSON.parse(JSON.stringify(initialMessages));
|
|
150
|
+
for (const result of toolResults) {
|
|
151
|
+
try {
|
|
152
|
+
if (!result?.messages) {
|
|
153
|
+
logger.error('Invalid tool result structure, skipping message history update');
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Add only the new messages from this tool's history
|
|
158
|
+
const newMessages = result.messages.slice(initialMessages.length);
|
|
159
|
+
finalMessages.push(...newMessages);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
logger.error(`Error merging message history for tool result: ${error.message}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Check if any tool calls failed
|
|
166
|
+
const failedTools = toolResults.filter(result => !result.success);
|
|
167
|
+
if (failedTools.length > 0) {
|
|
168
|
+
logger.warn(`Some tool calls failed: ${failedTools.map(t => t.error).join(', ')}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
args.chatHistory = finalMessages;
|
|
172
|
+
|
|
173
|
+
await pathwayResolver.promptAndParse({
|
|
174
|
+
...args,
|
|
175
|
+
tools: entityToolsOpenAiFormat,
|
|
176
|
+
tool_choice: "auto",
|
|
177
|
+
stream: true
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
executePathway: async ({args, runAllPrompts, resolver}) => {
|
|
183
|
+
let pathwayResolver = resolver;
|
|
184
|
+
|
|
185
|
+
// Load input parameters and information into args
|
|
186
|
+
const { entityId, voiceResponse, aiMemorySelfModify } = { ...pathwayResolver.pathway.inputParameters, ...args };
|
|
187
|
+
|
|
188
|
+
const entityConfig = loadEntityConfig(entityId);
|
|
189
|
+
const { entityTools, entityToolsOpenAiFormat } = getToolsForEntity(entityConfig);
|
|
190
|
+
const { useMemory: entityUseMemory = true, name: entityName, instructions: entityInstructions } = entityConfig || {};
|
|
191
|
+
|
|
192
|
+
if (entityId && entityName) {
|
|
193
|
+
args.aiName = entityName;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
args = {
|
|
197
|
+
...args,
|
|
198
|
+
...config.get('entityConstants'),
|
|
199
|
+
entityId,
|
|
200
|
+
entityTools,
|
|
201
|
+
entityToolsOpenAiFormat,
|
|
202
|
+
entityUseMemory,
|
|
203
|
+
entityInstructions,
|
|
204
|
+
voiceResponse,
|
|
205
|
+
aiMemorySelfModify
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
pathwayResolver.args = {...args};
|
|
209
|
+
|
|
210
|
+
const memoryTemplates = entityUseMemory ?
|
|
211
|
+
`{{renderTemplate AI_MEMORY}}\n\n{{renderTemplate AI_MEMORY_INSTRUCTIONS}}\n\n` : '';
|
|
212
|
+
|
|
213
|
+
const instructionTemplates = entityInstructions ? (entityInstructions + '\n\n') : `{{renderTemplate AI_EXPERTISE}}\n\n{{renderTemplate AI_COMMON_INSTRUCTIONS}}\n\n`;
|
|
214
|
+
|
|
215
|
+
const promptMessages = [
|
|
216
|
+
{"role": "system", "content": `${memoryTemplates}${instructionTemplates}{{renderTemplate AI_TOOLS}}\n\n{{renderTemplate AI_GROUNDING_INSTRUCTIONS}}\n\n{{renderTemplate AI_DATETIME}}`},
|
|
217
|
+
"{{chatHistory}}",
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
pathwayResolver.pathwayPrompt = [
|
|
221
|
+
new Prompt({ messages: promptMessages }),
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
// if the model has been overridden, make sure to use it
|
|
225
|
+
if (pathwayResolver.modelName) {
|
|
226
|
+
pathwayResolver.args.model = pathwayResolver.modelName;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// set the style model if applicable
|
|
230
|
+
const { aiStyle, AI_STYLE_ANTHROPIC, AI_STYLE_OPENAI } = args;
|
|
231
|
+
const styleModel = aiStyle === "Anthropic" ? AI_STYLE_ANTHROPIC : AI_STYLE_OPENAI;
|
|
232
|
+
|
|
233
|
+
// Initialize chat history if needed
|
|
234
|
+
if (!args.chatHistory || args.chatHistory.length === 0) {
|
|
235
|
+
args.chatHistory = [];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Limit the chat history to 20 messages to speed up processing
|
|
239
|
+
if (args.messages && args.messages.length > 0) {
|
|
240
|
+
args.chatHistory = args.messages.slice(-20);
|
|
241
|
+
} else {
|
|
242
|
+
args.chatHistory = args.chatHistory.slice(-20);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// remove old image and file content
|
|
246
|
+
const visionContentPresent = chatArgsHasImageUrl(args);
|
|
247
|
+
visionContentPresent && (args.chatHistory = removeOldImageAndFileContent(args.chatHistory));
|
|
248
|
+
|
|
249
|
+
// truncate the chat history in case there is really long content
|
|
250
|
+
const truncatedChatHistory = resolver.modelExecutor.plugin.truncateMessagesToTargetLength(args.chatHistory, null, 1000);
|
|
251
|
+
|
|
252
|
+
// Add the memory context to the chat history if applicable
|
|
253
|
+
if (truncatedChatHistory.length > 1 && entityUseMemory) {
|
|
254
|
+
const memoryContext = await callPathway('sys_read_memory', { ...args, chatHistory: truncatedChatHistory, section: 'memoryContext', priority: 0, recentHours: 0, stream: false }, resolver);
|
|
255
|
+
if (memoryContext) {
|
|
256
|
+
insertToolCallAndResults(args.chatHistory, "Load general memory context information", "LoadMemoryContext", memoryContext);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Asynchronously manage memory for this context
|
|
261
|
+
if (args.aiMemorySelfModify && entityUseMemory) {
|
|
262
|
+
callPathway('sys_memory_manager', { ...args, chatHistory: truncatedChatHistory, stream: false })
|
|
263
|
+
.catch(error => logger.error(error?.message || "Error in sys_memory_manager pathway"));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
let currentMessages = JSON.parse(JSON.stringify(args.chatHistory));
|
|
268
|
+
|
|
269
|
+
// Run the initial prompt with streaming
|
|
270
|
+
const response = await runAllPrompts({
|
|
271
|
+
...args,
|
|
272
|
+
chatHistory: currentMessages,
|
|
273
|
+
tools: entityToolsOpenAiFormat,
|
|
274
|
+
tool_choice: "auto",
|
|
275
|
+
stream: true
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
// Return the final response
|
|
280
|
+
return response;
|
|
281
|
+
|
|
282
|
+
} catch (e) {
|
|
283
|
+
resolver.logError(e);
|
|
284
|
+
const chatResponse = await callPathway('sys_generator_quick', {...args, model: styleModel, stream: false}, resolver);
|
|
285
|
+
resolver.tool = JSON.stringify({ search: false, title: args.title });
|
|
286
|
+
return chatResponse;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// sys_get_entities.js
|
|
2
|
+
// Pathway to get list of available entities with their tools
|
|
3
|
+
|
|
4
|
+
import { getAvailableEntities } from './tools/shared/sys_entity_tools.js';
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
prompt: [],
|
|
8
|
+
inputParameters: {},
|
|
9
|
+
executePathway: async ({ args }) => {
|
|
10
|
+
try {
|
|
11
|
+
const entities = getAvailableEntities();
|
|
12
|
+
return JSON.stringify(entities);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
return JSON.stringify(error);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
json: true, // We want JSON output
|
|
18
|
+
manageTokenLength: false, // No need to manage token length for this simple operation
|
|
19
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// sys_entity_tools.js
|
|
2
|
+
// Shared tool definitions that can be used by any entity
|
|
3
|
+
import { config } from '../../../../../config.js';
|
|
4
|
+
import logger from '../../../../../lib/logger.js';
|
|
5
|
+
|
|
6
|
+
export const CUSTOM_TOOLS = {};
|
|
7
|
+
|
|
8
|
+
// Helper function to get tools for a specific entity
|
|
9
|
+
export const getToolsForEntity = (entityConfig) => {
|
|
10
|
+
// Get system tools from config
|
|
11
|
+
const systemTools = config.get('entityTools') || {};
|
|
12
|
+
|
|
13
|
+
// Convert all tool names to lowercase in system tools
|
|
14
|
+
const normalizedSystemTools = Object.fromEntries(
|
|
15
|
+
Object.entries(systemTools).map(([key, value]) => [key.toLowerCase(), value])
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
// Convert custom tools to lowercase if they exist
|
|
19
|
+
const normalizedCustomTools = entityConfig?.customTools ?
|
|
20
|
+
Object.fromEntries(
|
|
21
|
+
Object.entries(entityConfig.customTools).map(([key, value]) => [key.toLowerCase(), value])
|
|
22
|
+
) : {};
|
|
23
|
+
|
|
24
|
+
// Convert CUSTOM_TOOLS to lowercase
|
|
25
|
+
const normalizedCUSTOM_TOOLS = Object.fromEntries(
|
|
26
|
+
Object.entries(CUSTOM_TOOLS).map(([key, value]) => [key.toLowerCase(), value])
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// Merge system tools with custom tools (custom tools override system tools)
|
|
30
|
+
const allTools = { ...normalizedSystemTools, ...normalizedCustomTools, ...normalizedCUSTOM_TOOLS };
|
|
31
|
+
|
|
32
|
+
// If no tools property specified or array contains *, return all tools
|
|
33
|
+
if (!entityConfig?.tools || entityConfig.tools.includes('*')) {
|
|
34
|
+
return {
|
|
35
|
+
entityTools: allTools,
|
|
36
|
+
entityToolsOpenAiFormat: Object.values(allTools).map(tool => {
|
|
37
|
+
const { icon, pathwayParams, ...definitionWithoutExtras } = tool.definition;
|
|
38
|
+
return definitionWithoutExtras;
|
|
39
|
+
})
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Get the list of tool names for this entity and convert to lowercase for case-insensitive comparison
|
|
44
|
+
const entityToolNames = entityConfig.tools.map(name => name.toLowerCase());
|
|
45
|
+
|
|
46
|
+
// Add custom tools to the list of allowed tools if they exist
|
|
47
|
+
if (entityConfig.customTools) {
|
|
48
|
+
Object.keys(entityConfig.customTools).forEach(toolName => {
|
|
49
|
+
if (!entityToolNames.includes(toolName.toLowerCase())) {
|
|
50
|
+
entityToolNames.push(toolName.toLowerCase());
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Filter the tools to only include those specified for this entity
|
|
56
|
+
const filteredTools = Object.fromEntries(
|
|
57
|
+
Object.entries(allTools).filter(([toolName]) =>
|
|
58
|
+
entityToolNames.includes(toolName.toLowerCase())
|
|
59
|
+
)
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
entityTools: filteredTools,
|
|
64
|
+
entityToolsOpenAiFormat: Object.values(filteredTools).map(tool => {
|
|
65
|
+
const { icon, pathwayParams, ...definitionWithoutExtras } = tool.definition;
|
|
66
|
+
return definitionWithoutExtras;
|
|
67
|
+
})
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Load entity configurations
|
|
72
|
+
export const loadEntityConfig = (entityId) => {
|
|
73
|
+
try {
|
|
74
|
+
entityId = entityId.toLowerCase();
|
|
75
|
+
|
|
76
|
+
const entityConfig = config.get('entityConfig');
|
|
77
|
+
if (!entityConfig) {
|
|
78
|
+
logger.warn('No entity config found in config');
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Handle both array and object formats
|
|
83
|
+
const configArray = Array.isArray(entityConfig) ? entityConfig : Object.entries(entityConfig).map(([id, config]) => ({
|
|
84
|
+
id,
|
|
85
|
+
...config
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
// If entityId is provided, look for that specific entity
|
|
89
|
+
if (entityId) {
|
|
90
|
+
const entity = configArray.find(e => e.id === entityId);
|
|
91
|
+
if (entity) {
|
|
92
|
+
return entity;
|
|
93
|
+
}
|
|
94
|
+
logger.warn(`Entity ${entityId} not found in config`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// If no entityId or entity not found, look for default entity
|
|
98
|
+
const defaultEntity = configArray.find(e => e.isDefault === true);
|
|
99
|
+
if (defaultEntity) {
|
|
100
|
+
return defaultEntity;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// If no default entity found, return the first entity
|
|
104
|
+
if (configArray.length > 0) {
|
|
105
|
+
return configArray[0];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return null;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
logger.error(`Error loading entity config: ${error.message}`);
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Fetches the list of available entities with their descriptions and active tools
|
|
117
|
+
* @returns {Array} Array of objects containing entity information and their active tools
|
|
118
|
+
*/
|
|
119
|
+
export const getAvailableEntities = () => {
|
|
120
|
+
try {
|
|
121
|
+
const entityConfig = config.get('entityConfig');
|
|
122
|
+
if (!entityConfig) {
|
|
123
|
+
logger.warn('No entity config found in config');
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Handle both array and object formats
|
|
128
|
+
const configArray = Array.isArray(entityConfig) ? entityConfig : Object.entries(entityConfig).map(([id, config]) => ({
|
|
129
|
+
id,
|
|
130
|
+
...config
|
|
131
|
+
}));
|
|
132
|
+
|
|
133
|
+
return configArray.map(entity => {
|
|
134
|
+
const { entityTools } = getToolsForEntity(entity);
|
|
135
|
+
return {
|
|
136
|
+
id: entity.id,
|
|
137
|
+
name: entity.name || entity.id,
|
|
138
|
+
description: entity.description || '',
|
|
139
|
+
isDefault: entity.isDefault || false,
|
|
140
|
+
activeTools: Object.keys(entityTools).map(toolName => ({
|
|
141
|
+
name: toolName,
|
|
142
|
+
description: entityTools[toolName].definition?.function?.description || ''
|
|
143
|
+
}))
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
} catch (error) {
|
|
147
|
+
logger.error(`Error fetching available entities: ${error.message}`);
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// sys_tool_bing_search.js
|
|
2
|
+
// Tool pathway that handles Bing web search functionality
|
|
3
|
+
import { callPathway } from '../../../../lib/pathwayTools.js';
|
|
4
|
+
import logger from '../../../../lib/logger.js';
|
|
5
|
+
import { config } from '../../../../config.js';
|
|
6
|
+
import { getSearchResultId } from '../../../../lib/util.js';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
prompt: [],
|
|
10
|
+
timeout: 300,
|
|
11
|
+
toolDefinition: {
|
|
12
|
+
type: "function",
|
|
13
|
+
icon: "🌐",
|
|
14
|
+
function: {
|
|
15
|
+
name: "InternetSearch",
|
|
16
|
+
description: "This tool allows you to use the Bing search api to search the internet and more. Use this for current events, news, fact-checking, and information requiring citation.",
|
|
17
|
+
parameters: {
|
|
18
|
+
type: "object",
|
|
19
|
+
properties: {
|
|
20
|
+
q: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "The complete query to pass to Azure Bing search. You can use advanced search operators in your query: + to require terms, \" \" for exact phrases, () for grouping, AND/& for requiring all terms, NOT/- to exclude terms, OR/| for either term. For example: '+(exact phrase) AND term1 -term2'"
|
|
23
|
+
},
|
|
24
|
+
freshness: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "Filter results by freshness (when the content was first encountered by the search engine). Only use this if you need to be very specific as it may exclude many relevant results. Can be 'day', 'week', 'month', or a date range 'YYYY-MM-DD..YYYY-MM-DD'"
|
|
27
|
+
},
|
|
28
|
+
count: {
|
|
29
|
+
type: "integer",
|
|
30
|
+
description: "Number of webpages to return (default is 10)"
|
|
31
|
+
},
|
|
32
|
+
safeSearch: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Filter adult content. Can be 'Off', 'Moderate' (default), or 'Strict'"
|
|
35
|
+
},
|
|
36
|
+
userMessage: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: "A user-friendly message that describes what you're doing with this tool"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
required: ["q", "userMessage"]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
executePathway: async ({args, runAllPrompts, resolver}) => {
|
|
47
|
+
|
|
48
|
+
// Check if Bing API key is available
|
|
49
|
+
const bingAvailable = !!config.getEnv()["AZURE_BING_KEY"];
|
|
50
|
+
if (!bingAvailable) {
|
|
51
|
+
throw new Error("Bing search is not available - missing API key");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
// Call the Bing search pathway
|
|
56
|
+
const response = await callPathway('bing', {
|
|
57
|
+
...args
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const parsedResponse = JSON.parse(response);
|
|
61
|
+
const results = [];
|
|
62
|
+
|
|
63
|
+
// Process web pages
|
|
64
|
+
if (parsedResponse.webPages && parsedResponse.webPages.value) {
|
|
65
|
+
results.push(...parsedResponse.webPages.value.map(({ name, url, snippet }) => ({
|
|
66
|
+
searchResultId: getSearchResultId(),
|
|
67
|
+
title: name,
|
|
68
|
+
url,
|
|
69
|
+
content: snippet
|
|
70
|
+
})));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Process computation results
|
|
74
|
+
if (parsedResponse.computation) {
|
|
75
|
+
results.push({
|
|
76
|
+
searchResultId: getSearchResultId(),
|
|
77
|
+
title: "Computation Result",
|
|
78
|
+
content: `Expression: ${parsedResponse.computation.expression}, Value: ${parsedResponse.computation.value}`
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Process entities
|
|
83
|
+
if (parsedResponse.entities && parsedResponse.entities.value) {
|
|
84
|
+
results.push(...parsedResponse.entities.value.map(entity => ({
|
|
85
|
+
searchResultId: getSearchResultId(),
|
|
86
|
+
title: entity.name,
|
|
87
|
+
content: entity.description,
|
|
88
|
+
url: entity.webSearchUrl
|
|
89
|
+
})));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Process news
|
|
93
|
+
if (parsedResponse.news && parsedResponse.news.value) {
|
|
94
|
+
results.push(...parsedResponse.news.value.map(news => ({
|
|
95
|
+
searchResultId: getSearchResultId(),
|
|
96
|
+
title: news.name,
|
|
97
|
+
content: news.description,
|
|
98
|
+
url: news.url
|
|
99
|
+
})));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Process videos
|
|
103
|
+
if (parsedResponse.videos && parsedResponse.videos.value) {
|
|
104
|
+
results.push(...parsedResponse.videos.value.map(video => ({
|
|
105
|
+
searchResultId: getSearchResultId(),
|
|
106
|
+
title: video.name,
|
|
107
|
+
content: video.description,
|
|
108
|
+
url: video.contentUrl
|
|
109
|
+
})));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Process places
|
|
113
|
+
if (parsedResponse.places && parsedResponse.places.value) {
|
|
114
|
+
results.push(...parsedResponse.places.value.map(place => ({
|
|
115
|
+
searchResultId: getSearchResultId(),
|
|
116
|
+
title: place.name,
|
|
117
|
+
content: `Address: ${place.address.addressLocality}, ${place.address.addressRegion}, ${place.address.addressCountry}`,
|
|
118
|
+
url: place.webSearchUrl
|
|
119
|
+
})));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Process time zone
|
|
123
|
+
if (parsedResponse.timeZone) {
|
|
124
|
+
results.push({
|
|
125
|
+
searchResultId: getSearchResultId(),
|
|
126
|
+
title: "Time Zone Information",
|
|
127
|
+
content: parsedResponse.timeZone.primaryResponse || parsedResponse.timeZone.description
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Process translations
|
|
132
|
+
if (parsedResponse.translations && parsedResponse.translations.value) {
|
|
133
|
+
results.push(...parsedResponse.translations.value.map(translation => ({
|
|
134
|
+
searchResultId: getSearchResultId(),
|
|
135
|
+
title: "Translation",
|
|
136
|
+
content: `Original (${translation.inLanguage}): ${translation.originalText}, Translated (${translation.translatedLanguageName}): ${translation.translatedText}`
|
|
137
|
+
})));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
resolver.tool = JSON.stringify({ toolUsed: "Search" });
|
|
141
|
+
return JSON.stringify({ _type: "SearchResponse", value: results });
|
|
142
|
+
} catch (e) {
|
|
143
|
+
logger.error(`Error in Bing search: ${e}`);
|
|
144
|
+
throw e;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|