@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.
Files changed (47) hide show
  1. package/README.md +9 -9
  2. package/config/default.example.json +0 -20
  3. package/config.js +160 -6
  4. package/lib/pathwayTools.js +79 -1
  5. package/lib/requestExecutor.js +3 -1
  6. package/lib/util.js +7 -0
  7. package/package.json +1 -1
  8. package/pathways/basePathway.js +2 -0
  9. package/pathways/call_tools.js +379 -0
  10. package/pathways/system/entity/memory/shared/sys_memory_helpers.js +1 -1
  11. package/pathways/system/entity/memory/sys_search_memory.js +2 -2
  12. package/pathways/system/entity/sys_entity_agent.js +289 -0
  13. package/pathways/system/entity/sys_generator_memory.js +1 -1
  14. package/pathways/system/entity/sys_generator_results.js +1 -1
  15. package/pathways/system/entity/sys_get_entities.js +19 -0
  16. package/pathways/system/entity/tools/shared/sys_entity_tools.js +150 -0
  17. package/pathways/system/entity/tools/sys_tool_bing_search.js +147 -0
  18. package/pathways/system/entity/tools/sys_tool_callmodel.js +62 -0
  19. package/pathways/system/entity/tools/sys_tool_coding.js +53 -0
  20. package/pathways/system/entity/tools/sys_tool_codingagent.js +100 -0
  21. package/pathways/system/entity/tools/sys_tool_cognitive_search.js +231 -0
  22. package/pathways/system/entity/tools/sys_tool_image.js +57 -0
  23. package/pathways/system/entity/tools/sys_tool_readfile.js +119 -0
  24. package/pathways/system/entity/tools/sys_tool_reasoning.js +75 -0
  25. package/pathways/system/entity/tools/sys_tool_remember.js +59 -0
  26. package/pathways/vision.js +1 -1
  27. package/server/modelExecutor.js +4 -12
  28. package/server/pathwayResolver.js +53 -40
  29. package/server/plugins/azureBingPlugin.js +42 -4
  30. package/server/plugins/azureCognitivePlugin.js +40 -12
  31. package/server/plugins/claude3VertexPlugin.js +67 -18
  32. package/server/plugins/modelPlugin.js +3 -2
  33. package/server/plugins/openAiReasoningPlugin.js +3 -3
  34. package/server/plugins/openAiReasoningVisionPlugin.js +48 -0
  35. package/server/plugins/openAiVisionPlugin.js +192 -7
  36. package/tests/agentic.test.js +256 -0
  37. package/tests/call_tools.test.js +216 -0
  38. package/tests/claude3VertexToolConversion.test.js +78 -0
  39. package/tests/mocks.js +11 -3
  40. package/tests/multimodal_conversion.test.js +1 -1
  41. package/tests/openAiToolPlugin.test.js +242 -0
  42. package/pathways/test_palm_chat.js +0 -31
  43. package/server/plugins/palmChatPlugin.js +0 -233
  44. package/server/plugins/palmCodeCompletionPlugin.js +0 -45
  45. package/server/plugins/palmCompletionPlugin.js +0 -135
  46. package/tests/palmChatPlugin.test.js +0 -219
  47. 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
+ };
@@ -10,7 +10,7 @@ export default {
10
10
  aiName: "Jarvis",
11
11
  language: "English",
12
12
  },
13
- model: 'oai-gpt4o',
13
+ model: 'oai-gpt41-mini',
14
14
  useInputChunking: false,
15
15
  enableDuplicateRequests: false,
16
16
  executePathway: async ({args, resolver}) => {
@@ -25,7 +25,7 @@ export default {
25
25
  language: "English",
26
26
  chatId: ``,
27
27
  dataSources: [""],
28
- model: 'oai-gpt4o',
28
+ model: 'oai-gpt41-mini',
29
29
  },
30
30
  timeout: 300,
31
31
 
@@ -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
+ };