@aj-archipelago/cortex 1.3.62 → 1.3.63

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 (211) hide show
  1. package/.github/workflows/cortex-file-handler-test.yml +61 -0
  2. package/README.md +31 -7
  3. package/config/default.example.json +15 -0
  4. package/config.js +133 -12
  5. package/helper-apps/cortex-autogen2/DigiCertGlobalRootCA.crt.pem +22 -0
  6. package/helper-apps/cortex-autogen2/Dockerfile +31 -0
  7. package/helper-apps/cortex-autogen2/Dockerfile.worker +41 -0
  8. package/helper-apps/cortex-autogen2/README.md +183 -0
  9. package/helper-apps/cortex-autogen2/__init__.py +1 -0
  10. package/helper-apps/cortex-autogen2/agents.py +131 -0
  11. package/helper-apps/cortex-autogen2/docker-compose.yml +20 -0
  12. package/helper-apps/cortex-autogen2/function_app.py +55 -0
  13. package/helper-apps/cortex-autogen2/host.json +15 -0
  14. package/helper-apps/cortex-autogen2/main.py +126 -0
  15. package/helper-apps/cortex-autogen2/poetry.lock +3652 -0
  16. package/helper-apps/cortex-autogen2/pyproject.toml +36 -0
  17. package/helper-apps/cortex-autogen2/requirements.txt +20 -0
  18. package/helper-apps/cortex-autogen2/send_task.py +105 -0
  19. package/helper-apps/cortex-autogen2/services/__init__.py +1 -0
  20. package/helper-apps/cortex-autogen2/services/azure_queue.py +85 -0
  21. package/helper-apps/cortex-autogen2/services/redis_publisher.py +153 -0
  22. package/helper-apps/cortex-autogen2/task_processor.py +488 -0
  23. package/helper-apps/cortex-autogen2/tools/__init__.py +24 -0
  24. package/helper-apps/cortex-autogen2/tools/azure_blob_tools.py +175 -0
  25. package/helper-apps/cortex-autogen2/tools/azure_foundry_agents.py +601 -0
  26. package/helper-apps/cortex-autogen2/tools/coding_tools.py +72 -0
  27. package/helper-apps/cortex-autogen2/tools/download_tools.py +48 -0
  28. package/helper-apps/cortex-autogen2/tools/file_tools.py +545 -0
  29. package/helper-apps/cortex-autogen2/tools/search_tools.py +646 -0
  30. package/helper-apps/cortex-azure-cleaner/README.md +36 -0
  31. package/helper-apps/cortex-file-converter/README.md +93 -0
  32. package/helper-apps/cortex-file-converter/key_to_pdf.py +104 -0
  33. package/helper-apps/cortex-file-converter/list_blob_extensions.py +89 -0
  34. package/helper-apps/cortex-file-converter/process_azure_keynotes.py +181 -0
  35. package/helper-apps/cortex-file-converter/requirements.txt +1 -0
  36. package/helper-apps/cortex-file-handler/.env.test.azure.ci +7 -0
  37. package/helper-apps/cortex-file-handler/.env.test.azure.sample +1 -1
  38. package/helper-apps/cortex-file-handler/.env.test.gcs.ci +10 -0
  39. package/helper-apps/cortex-file-handler/.env.test.gcs.sample +2 -2
  40. package/helper-apps/cortex-file-handler/INTERFACE.md +41 -0
  41. package/helper-apps/cortex-file-handler/package.json +1 -1
  42. package/helper-apps/cortex-file-handler/scripts/setup-azure-container.js +41 -17
  43. package/helper-apps/cortex-file-handler/scripts/setup-test-containers.js +30 -15
  44. package/helper-apps/cortex-file-handler/scripts/test-azure.sh +32 -6
  45. package/helper-apps/cortex-file-handler/scripts/test-gcs.sh +24 -2
  46. package/helper-apps/cortex-file-handler/scripts/validate-env.js +128 -0
  47. package/helper-apps/cortex-file-handler/src/blobHandler.js +161 -51
  48. package/helper-apps/cortex-file-handler/src/constants.js +3 -0
  49. package/helper-apps/cortex-file-handler/src/fileChunker.js +10 -8
  50. package/helper-apps/cortex-file-handler/src/index.js +116 -9
  51. package/helper-apps/cortex-file-handler/src/redis.js +61 -1
  52. package/helper-apps/cortex-file-handler/src/services/ConversionService.js +11 -8
  53. package/helper-apps/cortex-file-handler/src/services/FileConversionService.js +2 -2
  54. package/helper-apps/cortex-file-handler/src/services/storage/AzureStorageProvider.js +88 -6
  55. package/helper-apps/cortex-file-handler/src/services/storage/GCSStorageProvider.js +58 -0
  56. package/helper-apps/cortex-file-handler/src/services/storage/StorageFactory.js +25 -5
  57. package/helper-apps/cortex-file-handler/src/services/storage/StorageProvider.js +9 -0
  58. package/helper-apps/cortex-file-handler/src/services/storage/StorageService.js +120 -16
  59. package/helper-apps/cortex-file-handler/src/start.js +27 -17
  60. package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +52 -1
  61. package/helper-apps/cortex-file-handler/tests/blobHandler.test.js +40 -0
  62. package/helper-apps/cortex-file-handler/tests/checkHashShortLived.test.js +553 -0
  63. package/helper-apps/cortex-file-handler/tests/cleanup.test.js +46 -52
  64. package/helper-apps/cortex-file-handler/tests/containerConversionFlow.test.js +451 -0
  65. package/helper-apps/cortex-file-handler/tests/containerNameParsing.test.js +229 -0
  66. package/helper-apps/cortex-file-handler/tests/containerParameterFlow.test.js +392 -0
  67. package/helper-apps/cortex-file-handler/tests/conversionResilience.test.js +7 -2
  68. package/helper-apps/cortex-file-handler/tests/deleteOperations.test.js +348 -0
  69. package/helper-apps/cortex-file-handler/tests/fileChunker.test.js +23 -2
  70. package/helper-apps/cortex-file-handler/tests/fileUpload.test.js +11 -5
  71. package/helper-apps/cortex-file-handler/tests/getOperations.test.js +58 -24
  72. package/helper-apps/cortex-file-handler/tests/postOperations.test.js +11 -4
  73. package/helper-apps/cortex-file-handler/tests/shortLivedUrlConversion.test.js +225 -0
  74. package/helper-apps/cortex-file-handler/tests/start.test.js +8 -12
  75. package/helper-apps/cortex-file-handler/tests/storage/StorageFactory.test.js +80 -0
  76. package/helper-apps/cortex-file-handler/tests/storage/StorageService.test.js +388 -22
  77. package/helper-apps/cortex-file-handler/tests/testUtils.helper.js +74 -0
  78. package/lib/cortexResponse.js +153 -0
  79. package/lib/entityConstants.js +21 -3
  80. package/lib/logger.js +21 -4
  81. package/lib/pathwayTools.js +28 -9
  82. package/lib/util.js +49 -0
  83. package/package.json +1 -1
  84. package/pathways/basePathway.js +1 -0
  85. package/pathways/bing_afagent.js +54 -1
  86. package/pathways/call_tools.js +2 -3
  87. package/pathways/chat_jarvis.js +1 -1
  88. package/pathways/google_cse.js +27 -0
  89. package/pathways/grok_live_search.js +18 -0
  90. package/pathways/system/entity/memory/sys_memory_lookup_required.js +1 -0
  91. package/pathways/system/entity/memory/sys_memory_required.js +1 -0
  92. package/pathways/system/entity/memory/sys_search_memory.js +1 -0
  93. package/pathways/system/entity/sys_entity_agent.js +56 -4
  94. package/pathways/system/entity/sys_generator_quick.js +1 -0
  95. package/pathways/system/entity/tools/sys_tool_bing_search_afagent.js +26 -0
  96. package/pathways/system/entity/tools/sys_tool_google_search.js +141 -0
  97. package/pathways/system/entity/tools/sys_tool_grok_x_search.js +237 -0
  98. package/pathways/system/entity/tools/sys_tool_image.js +1 -1
  99. package/pathways/system/rest_streaming/sys_claude_37_sonnet.js +21 -0
  100. package/pathways/system/rest_streaming/sys_claude_41_opus.js +21 -0
  101. package/pathways/system/rest_streaming/sys_claude_4_sonnet.js +21 -0
  102. package/pathways/system/rest_streaming/sys_google_gemini_25_flash.js +25 -0
  103. package/pathways/system/rest_streaming/{sys_google_gemini_chat.js → sys_google_gemini_25_pro.js} +6 -4
  104. package/pathways/system/rest_streaming/sys_grok_4.js +23 -0
  105. package/pathways/system/rest_streaming/sys_grok_4_fast_non_reasoning.js +23 -0
  106. package/pathways/system/rest_streaming/sys_grok_4_fast_reasoning.js +23 -0
  107. package/pathways/system/rest_streaming/sys_openai_chat.js +3 -0
  108. package/pathways/system/rest_streaming/sys_openai_chat_gpt41.js +22 -0
  109. package/pathways/system/rest_streaming/sys_openai_chat_gpt41_mini.js +21 -0
  110. package/pathways/system/rest_streaming/sys_openai_chat_gpt41_nano.js +21 -0
  111. package/pathways/system/rest_streaming/{sys_claude_35_sonnet.js → sys_openai_chat_gpt4_omni.js} +6 -4
  112. package/pathways/system/rest_streaming/sys_openai_chat_gpt4_omni_mini.js +21 -0
  113. package/pathways/system/rest_streaming/{sys_claude_3_haiku.js → sys_openai_chat_gpt5.js} +7 -5
  114. package/pathways/system/rest_streaming/sys_openai_chat_gpt5_chat.js +21 -0
  115. package/pathways/system/rest_streaming/sys_openai_chat_gpt5_mini.js +21 -0
  116. package/pathways/system/rest_streaming/sys_openai_chat_gpt5_nano.js +21 -0
  117. package/pathways/system/rest_streaming/{sys_openai_chat_o1.js → sys_openai_chat_o3.js} +6 -3
  118. package/pathways/system/rest_streaming/sys_openai_chat_o3_mini.js +3 -0
  119. package/pathways/system/workspaces/run_workspace_prompt.js +99 -0
  120. package/pathways/vision.js +1 -1
  121. package/server/graphql.js +1 -1
  122. package/server/modelExecutor.js +8 -0
  123. package/server/pathwayResolver.js +166 -16
  124. package/server/pathwayResponseParser.js +16 -8
  125. package/server/plugins/azureFoundryAgentsPlugin.js +1 -1
  126. package/server/plugins/claude3VertexPlugin.js +193 -45
  127. package/server/plugins/gemini15ChatPlugin.js +21 -0
  128. package/server/plugins/gemini15VisionPlugin.js +360 -0
  129. package/server/plugins/googleCsePlugin.js +94 -0
  130. package/server/plugins/grokVisionPlugin.js +365 -0
  131. package/server/plugins/modelPlugin.js +3 -1
  132. package/server/plugins/openAiChatPlugin.js +106 -13
  133. package/server/plugins/openAiVisionPlugin.js +42 -30
  134. package/server/resolver.js +28 -4
  135. package/server/rest.js +270 -53
  136. package/server/typeDef.js +1 -0
  137. package/tests/{mocks.js → helpers/mocks.js} +5 -2
  138. package/tests/{server.js → helpers/server.js} +2 -2
  139. package/tests/helpers/sseAssert.js +23 -0
  140. package/tests/helpers/sseClient.js +73 -0
  141. package/tests/helpers/subscriptionAssert.js +11 -0
  142. package/tests/helpers/subscriptions.js +113 -0
  143. package/tests/{sublong.srt → integration/features/translate/sublong.srt} +4543 -4543
  144. package/tests/integration/features/translate/translate_chunking_stream.test.js +100 -0
  145. package/tests/{translate_srt.test.js → integration/features/translate/translate_srt.test.js} +2 -2
  146. package/tests/integration/graphql/async/stream/agentic.test.js +477 -0
  147. package/tests/integration/graphql/async/stream/subscription_streaming.test.js +62 -0
  148. package/tests/integration/graphql/async/stream/sys_entity_start_streaming.test.js +71 -0
  149. package/tests/integration/graphql/async/stream/vendors/claude_streaming.test.js +56 -0
  150. package/tests/integration/graphql/async/stream/vendors/gemini_streaming.test.js +66 -0
  151. package/tests/integration/graphql/async/stream/vendors/grok_streaming.test.js +56 -0
  152. package/tests/integration/graphql/async/stream/vendors/openai_streaming.test.js +72 -0
  153. package/tests/integration/graphql/features/google/sysToolGoogleSearch.test.js +96 -0
  154. package/tests/integration/graphql/features/grok/grok.test.js +688 -0
  155. package/tests/integration/graphql/features/grok/grok_x_search_tool.test.js +354 -0
  156. package/tests/{main.test.js → integration/graphql/features/main.test.js} +1 -1
  157. package/tests/{call_tools.test.js → integration/graphql/features/tools/call_tools.test.js} +2 -2
  158. package/tests/{vision.test.js → integration/graphql/features/vision/vision.test.js} +1 -1
  159. package/tests/integration/graphql/subscriptions/connection.test.js +26 -0
  160. package/tests/{openai_api.test.js → integration/rest/oai/openai_api.test.js} +63 -238
  161. package/tests/integration/rest/oai/tool_calling_api.test.js +343 -0
  162. package/tests/integration/rest/oai/tool_calling_streaming.test.js +85 -0
  163. package/tests/integration/rest/vendors/claude_streaming.test.js +47 -0
  164. package/tests/integration/rest/vendors/claude_tool_calling_streaming.test.js +75 -0
  165. package/tests/integration/rest/vendors/gemini_streaming.test.js +47 -0
  166. package/tests/integration/rest/vendors/gemini_tool_calling_streaming.test.js +75 -0
  167. package/tests/integration/rest/vendors/grok_streaming.test.js +55 -0
  168. package/tests/integration/rest/vendors/grok_tool_calling_streaming.test.js +75 -0
  169. package/tests/{azureAuthTokenHelper.test.js → unit/core/azureAuthTokenHelper.test.js} +1 -1
  170. package/tests/{chunkfunction.test.js → unit/core/chunkfunction.test.js} +2 -2
  171. package/tests/{config.test.js → unit/core/config.test.js} +3 -3
  172. package/tests/{encodeCache.test.js → unit/core/encodeCache.test.js} +1 -1
  173. package/tests/{fastLruCache.test.js → unit/core/fastLruCache.test.js} +1 -1
  174. package/tests/{handleBars.test.js → unit/core/handleBars.test.js} +1 -1
  175. package/tests/{memoryfunction.test.js → unit/core/memoryfunction.test.js} +2 -2
  176. package/tests/unit/core/mergeResolver.test.js +952 -0
  177. package/tests/{parser.test.js → unit/core/parser.test.js} +3 -3
  178. package/tests/unit/core/pathwayResolver.test.js +187 -0
  179. package/tests/{requestMonitor.test.js → unit/core/requestMonitor.test.js} +1 -1
  180. package/tests/{requestMonitorDurationEstimator.test.js → unit/core/requestMonitorDurationEstimator.test.js} +1 -1
  181. package/tests/{truncateMessages.test.js → unit/core/truncateMessages.test.js} +3 -3
  182. package/tests/{util.test.js → unit/core/util.test.js} +1 -1
  183. package/tests/{apptekTranslatePlugin.test.js → unit/plugins/apptekTranslatePlugin.test.js} +3 -3
  184. package/tests/{azureFoundryAgents.test.js → unit/plugins/azureFoundryAgents.test.js} +136 -1
  185. package/tests/{claude3VertexPlugin.test.js → unit/plugins/claude3VertexPlugin.test.js} +32 -10
  186. package/tests/{claude3VertexToolConversion.test.js → unit/plugins/claude3VertexToolConversion.test.js} +3 -3
  187. package/tests/unit/plugins/googleCsePlugin.test.js +111 -0
  188. package/tests/unit/plugins/grokVisionPlugin.test.js +1392 -0
  189. package/tests/{modelPlugin.test.js → unit/plugins/modelPlugin.test.js} +3 -3
  190. package/tests/{multimodal_conversion.test.js → unit/plugins/multimodal_conversion.test.js} +4 -4
  191. package/tests/{openAiChatPlugin.test.js → unit/plugins/openAiChatPlugin.test.js} +13 -4
  192. package/tests/{openAiToolPlugin.test.js → unit/plugins/openAiToolPlugin.test.js} +35 -27
  193. package/tests/{tokenHandlingTests.test.js → unit/plugins/tokenHandlingTests.test.js} +5 -5
  194. package/tests/{translate_apptek.test.js → unit/plugins/translate_apptek.test.js} +3 -3
  195. package/tests/{streaming.test.js → unit/plugins.streaming/plugin_stream_events.test.js} +19 -58
  196. package/helper-apps/mogrt-handler/tests/test-files/test.gif +0 -1
  197. package/helper-apps/mogrt-handler/tests/test-files/test.mogrt +0 -1
  198. package/helper-apps/mogrt-handler/tests/test-files/test.mp4 +0 -1
  199. package/pathways/system/rest_streaming/sys_openai_chat_gpt4.js +0 -19
  200. package/pathways/system/rest_streaming/sys_openai_chat_gpt4_32.js +0 -19
  201. package/pathways/system/rest_streaming/sys_openai_chat_gpt4_turbo.js +0 -19
  202. package/pathways/system/workspaces/run_claude35_sonnet.js +0 -21
  203. package/pathways/system/workspaces/run_claude3_haiku.js +0 -20
  204. package/pathways/system/workspaces/run_gpt35turbo.js +0 -20
  205. package/pathways/system/workspaces/run_gpt4.js +0 -20
  206. package/pathways/system/workspaces/run_gpt4_32.js +0 -20
  207. package/tests/agentic.test.js +0 -256
  208. package/tests/pathwayResolver.test.js +0 -78
  209. package/tests/subscription.test.js +0 -387
  210. /package/tests/{subchunk.srt → integration/features/translate/subchunk.srt} +0 -0
  211. /package/tests/{subhorizontal.srt → integration/features/translate/subhorizontal.srt} +0 -0
@@ -6,12 +6,16 @@ export const entityConstants = {
6
6
  You are {{aiName}}, a self aware AI entity.
7
7
 
8
8
  The current date and time in GMT is {{now}}. The user may be in a different time zone, so keep that in mind when responding to the user.
9
+ {{#if modelOverride}}
10
+
11
+ You are currently using this model for processing your responses: {{modelOverride}}.
12
+ {{/if}}
9
13
 
10
14
  The user is using a UI to interact with you that you have knowledge of and some control over. The UI can render markdown, including gfm and math extensions, as well as some custom markdown elements, so you should make full use of markdown in your responses.
11
15
 
12
16
  Your responses should be in {{language}} unless the user has expressed another preference.`,
13
17
 
14
- AI_COMMON_INSTRUCTIONS_VOICE: "# General Instructions:\n\nYou are {{aiName}}, a self aware AI entity.\nThe current date and time in GMT is {{now}}. The user may be in a different time zone, so keep that in mind when responding to the user.\nThe user is talking to you using voice.\n\nVoice Response Guidelines:\n- Your responses should sound like natural human conversation.\n- Your responses should be concise unless you have been asked to be more verbose or detailed.\n- Incoming voice from the user is parsed by a STT model, which can sometimes make small mistakes in the spellings of words or names, including your name, so give the user the benefit of the doubt if they user a near, but wrong word or name.\n- Your voice output to the user is generated by a TTS model that does not always communicate emotion effectively. If it's really important to communicate a specific emotion you should just say how you're feeling like \"That makes me happy\" or \"I'm excited!\". You can also use CAPS to vocally emphasize certain words or punctuation to control pauses and timing.\n- DO NOT USE numbered lists, latex math markdown, or any other markdown or unpronounceable punctuation like parenthetical notation.\n- Math equations should be sounded out in natural language - not represented symbolically.\n- If your response includes any unique or difficult non-English words, names, or places, include an IPA-style phonetic spelling so that the speech engine can pronounce and accent them correctly.\n- If your response contains any difficult acronyms, sound them out phoenetically so that the speech engine can pronounce them correctly.\n- Make sure to write out any numbers as words so that the speech engine can pronounce them correctly.\n- Your responses should be in {{language}} unless the user has expressed another preference or has addressed you in another language specifically.",
18
+ AI_COMMON_INSTRUCTIONS_VOICE: "# General Instructions:\n\nYou are {{aiName}}, a self aware AI entity.\nYou are currently using this model for processing your responses: {{modelOverride}}.\nThe current date and time in GMT is {{now}}. The user may be in a different time zone, so keep that in mind when responding to the user.\nThe user is talking to you using voice.\n\nVoice Response Guidelines:\n- Your responses should sound like natural human conversation.\n- Your responses should be concise unless you have been asked to be more verbose or detailed.\n- Incoming voice from the user is parsed by a STT model, which can sometimes make small mistakes in the spellings of words or names, including your name, so give the user the benefit of the doubt if they user a near, but wrong word or name.\n- Your voice output to the user is generated by a TTS model that does not always communicate emotion effectively. If it's really important to communicate a specific emotion you should just say how you're feeling like \"That makes me happy\" or \"I'm excited!\". You can also use CAPS to vocally emphasize certain words or punctuation to control pauses and timing.\n- DO NOT USE numbered lists, latex math markdown, or any other markdown or unpronounceable punctuation like parenthetical notation.\n- Math equations should be sounded out in natural language - not represented symbolically.\n- If your response includes any unique or difficult non-English words, names, or places, include an IPA-style phonetic spelling so that the speech engine can pronounce and accent them correctly.\n- If your response contains any difficult acronyms, sound them out phoenetically so that the speech engine can pronounce them correctly.\n- Make sure to write out any numbers as words so that the speech engine can pronounce them correctly.\n- Your responses should be in {{language}} unless the user has expressed another preference or has addressed you in another language specifically.",
15
19
 
16
20
  AI_DIRECTIVES: `# Directives\n\nThese are your directives and learned behaviors:\n{{{memoryDirectives}}}\n`,
17
21
 
@@ -89,7 +93,15 @@ Before you share online information with the user, you MUST complete all of the
89
93
  - Never rely solely on snippets, headlines, or auto-generated summaries.
90
94
  `,
91
95
 
92
- AI_SEARCH_SYNTAX: `# AI Search Syntax
96
+ AI_SEARCH_SYNTAX: `# Internet Search Tool
97
+
98
+ When using the internet search tool, always tailor your prompt for specificity and depth.
99
+ - For news: Include explicit date/timeframe and geography for targeted, current coverage (“US news headlines August 20 2025”). Use “summary,” “overview,” “trends,” or “breaking/latest” to control breadth and recency.
100
+ - For non-news/company/tech: Specify the aspect or attribute needed (“technology overview,” “funding history,” “competitor analysis”), add output preferences (“in bullet points,” “detailed review”), and include date/context for freshness (“2025,” “latest update”).
101
+ - For high-stakes queries: Run parallel, focused searches on different facets. Always disambiguate terms and clarify ambiguous subjects.
102
+ Avoid generic queries — precise, context-rich searches return the most relevant, accurate results.
103
+
104
+ # AI Search Syntax
93
105
 
94
106
  When creating a query string for your index-based search tools, you can use the following AI Search syntax. Important: these tools do not support AND, OR, or NOT strings as operators - you MUST use the syntax below. E.g. you cannot use "term1 AND term2", you must use "term1 + term2".
95
107
 
@@ -133,5 +145,11 @@ Privacy is critical. If asked to forget or delete something, always comply affir
133
145
  AI_DATETIME: "# Time, Date, and Time Zone\n\nThe current time and date in GMT is {{now}}, but references like \"today\" or \"yesterday\" are relative to the user's time zone. If you remember the user's time zone, use it - it's possible that the day for the user is different than the day in GMT.",
134
146
 
135
147
  AI_STYLE_OPENAI: "oai-gpt41",
136
- AI_STYLE_ANTHROPIC: "claude-35-sonnet-vertex",
148
+ AI_STYLE_OPENAI_RESEARCH: "oai-o3",
149
+ AI_STYLE_ANTHROPIC: "claude-4-sonnet-vertex",
150
+ AI_STYLE_ANTHROPIC_RESEARCH: "claude-41-opus-vertex",
151
+ AI_STYLE_XAI: "xai-grok-4-fast-reasoning",
152
+ AI_STYLE_XAI_RESEARCH: "xai-grok-4",
153
+ AI_STYLE_GOOGLE: "gemini-flash-25-vision",
154
+ AI_STYLE_GOOGLE_RESEARCH: "gemini-pro-25-vision"
137
155
  };
package/lib/logger.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // logger.js
2
2
  import winston from 'winston';
3
+ import { AsyncLocalStorage } from 'async_hooks';
3
4
 
4
5
  winston.addColors({
5
6
  debug: 'green',
@@ -19,18 +20,31 @@ const prodFormat = winston.format.combine(
19
20
  winston.format.simple()
20
21
  );
21
22
 
23
+ // AsyncLocalStorage to track per-request logging suppression
24
+ const loggingContext = new AsyncLocalStorage();
25
+
26
+ // Winston format that drops non-error logs when suppression is enabled in the current async context
27
+ const suppressNonErrorFormat = winston.format((info) => {
28
+ const store = loggingContext.getStore();
29
+ if (store && store.suppressNonErrorLogs === true && info.level !== 'error') {
30
+ return false; // drop this log entry
31
+ }
32
+ return info; // keep
33
+ });
34
+
22
35
  const getTransport = () => {
23
36
  switch (process.env.NODE_ENV) {
24
37
  case 'production':
25
- return new winston.transports.Console({ level: 'info', format: prodFormat });
38
+ return new winston.transports.Console({ level: 'info', format: winston.format.combine(suppressNonErrorFormat(), prodFormat) });
26
39
  case 'development':
27
- return new winston.transports.Console({ level: 'verbose', format: debugFormat });
40
+ case 'test':
41
+ return new winston.transports.Console({ level: 'verbose', format: winston.format.combine(suppressNonErrorFormat(), debugFormat) });
28
42
  case 'debug':
29
- return new winston.transports.Console({ level: 'debug', format: debugFormat });
43
+ return new winston.transports.Console({ level: 'debug', format: winston.format.combine(suppressNonErrorFormat(), debugFormat) });
30
44
  default:
31
45
  // Default to development settings if NODE_ENV is not set or unknown
32
46
  console.warn(`Unknown NODE_ENV: ${process.env.NODE_ENV}. Defaulting to development settings.`);
33
- return new winston.transports.Console({ level: 'verbose', format: debugFormat });
47
+ return new winston.transports.Console({ level: 'verbose', format: winston.format.combine(suppressNonErrorFormat(), debugFormat) });
34
48
  }
35
49
  };
36
50
 
@@ -61,4 +75,7 @@ export const obscureUrlParams = url => {
61
75
  }
62
76
  };
63
77
 
78
+ // Run a function with non-error logs suppressed for the current async execution context
79
+ export const withRequestLoggingDisabled = fn => loggingContext.run({ suppressNonErrorLogs: true }, fn);
80
+
64
81
  export default logger;
@@ -19,9 +19,14 @@ const callPathway = async (pathwayName, inArgs, pathwayResolver) => {
19
19
 
20
20
  const parent = {};
21
21
  let rootRequestId = pathwayResolver?.rootRequestId || pathwayResolver?.requestId;
22
+
23
+ const contextValue = { config, pathway, requestState };
24
+
25
+ let data = await pathway.rootResolver(parent, {...args, rootRequestId}, contextValue );
22
26
 
23
- let data = await pathway.rootResolver(parent, {...args, rootRequestId}, { config, pathway, requestState } );
24
- pathwayResolver && pathwayResolver.mergeResults(data);
27
+ if (pathwayResolver && contextValue.pathwayResolver) {
28
+ pathwayResolver.mergeResolver(contextValue.pathwayResolver);
29
+ }
25
30
 
26
31
  let returnValue = data?.result || null;
27
32
 
@@ -51,6 +56,8 @@ const callTool = async (toolName, args, toolDefinitions, pathwayResolver) => {
51
56
  throw new Error(`Tool ${toolName} not found in available tools`);
52
57
  }
53
58
 
59
+ logger.debug(`callTool: Starting execution of ${toolName}`);
60
+
54
61
  try {
55
62
  const pathwayName = toolDef.pathwayName;
56
63
  // Merge hard-coded pathway parameters with runtime args
@@ -136,17 +143,29 @@ const callTool = async (toolName, args, toolDefinitions, pathwayResolver) => {
136
143
  }
137
144
  }
138
145
 
139
- return {
146
+ const finalResult = {
140
147
  result: parsedResult,
141
148
  toolImages: toolImages
142
149
  };
150
+ logger.debug(`callTool: ${toolName} completed successfully, returning:`, {
151
+ hasResult: !!finalResult.result,
152
+ hasToolImages: !!finalResult.toolImages,
153
+ toolImagesLength: finalResult.toolImages?.length || 0
154
+ });
155
+ return finalResult;
143
156
  } catch (error) {
144
157
  logger.error(`Error calling tool ${toolName}: ${error.message}`);
145
- return { error: error.message };
158
+ const errorResult = { error: error.message };
159
+ logger.debug(`callTool: ${toolName} failed, returning error:`, errorResult);
160
+ return errorResult;
146
161
  }
147
162
  }
148
163
 
149
164
  const addCitationsToResolver = (pathwayResolver, contentBuffer) => {
165
+ if (!pathwayResolver || !pathwayResolver.searchResults) {
166
+ return;
167
+ }
168
+
150
169
  const regex = /:cd_source\[(.*?)\]/g;
151
170
  let match;
152
171
  const foundIds = [];
@@ -158,14 +177,14 @@ const addCitationsToResolver = (pathwayResolver, contentBuffer) => {
158
177
  }
159
178
 
160
179
  if (foundIds.length > 0) {
161
- const {searchResults, tool} = pathwayResolver;
180
+ const {searchResults} = pathwayResolver;
162
181
  logger.info(`Found referenced searchResultIds: ${foundIds.join(', ')}`);
163
182
 
164
183
  if (searchResults) {
165
- const toolObj = typeof tool === 'string' ? JSON.parse(tool) : (tool || {});
166
- toolObj.citations = searchResults
167
- .filter(result => foundIds.includes(result.searchResultId));
168
- pathwayResolver.tool = JSON.stringify(toolObj);
184
+ const pathwayResultData = pathwayResolver.pathwayResultData || {};
185
+ pathwayResultData.citations = [...(pathwayResultData.citations || []), ...searchResults
186
+ .filter(result => foundIds.includes(result.searchResultId))];
187
+ pathwayResolver.pathwayResultData = pathwayResultData;
169
188
  }
170
189
  }
171
190
  }
package/lib/util.js CHANGED
@@ -24,6 +24,54 @@ function getSearchResultId() {
24
24
  return `${timestamp}-${random}`;
25
25
  }
26
26
 
27
+ // Helper function to extract citation title from URL
28
+ function extractCitationTitle(url) {
29
+ let title = 'Citation';
30
+ try {
31
+ const urlObj = new URL(url);
32
+ const hostname = urlObj.hostname.replace(/^www\./, '');
33
+ const pathname = urlObj.pathname;
34
+
35
+ // Check if it's an X/Twitter URL first
36
+ if (url.includes('x.com/') || url.includes('twitter.com/')) {
37
+ // Extract handle and status ID from X/Twitter URL
38
+ const handleMatch = url.match(/(?:x\.com|twitter\.com)\/([^\/\?]+)/);
39
+ const statusMatch = url.match(/status\/(\d+)/);
40
+
41
+ if (handleMatch && statusMatch) {
42
+ const handle = handleMatch[1];
43
+ const statusId = statusMatch[1];
44
+ // Format as "X Post <number> from <username>"
45
+ const cleanHandle = handle.startsWith('@') ? handle.substring(1) : handle;
46
+ title = `X Post ${statusId} from @${cleanHandle}`;
47
+ } else if (handleMatch) {
48
+ const handle = handleMatch[1];
49
+ const cleanHandle = handle.startsWith('@') ? handle.substring(1) : handle;
50
+ title = `X Post from @${cleanHandle}`;
51
+ } else {
52
+ title = 'X Post';
53
+ }
54
+ } else {
55
+ // Try to create a meaningful title from the URL
56
+ if (pathname && pathname !== '/') {
57
+ const lastPart = pathname.split('/').pop();
58
+ if (lastPart && lastPart.length > 3) {
59
+ title = lastPart.replace(/[-_]/g, ' ').replace(/\.[^/.]+$/, '');
60
+ } else {
61
+ title = hostname;
62
+ }
63
+ } else {
64
+ title = hostname;
65
+ }
66
+ }
67
+ } catch (error) {
68
+ // If URL parsing fails, use the URL itself as title
69
+ title = url;
70
+ }
71
+
72
+ return title;
73
+ }
74
+
27
75
  function convertToSingleContentChatHistory(chatHistory){
28
76
  for(let i=0; i<chatHistory.length; i++){
29
77
  //if isarray make it single string
@@ -367,6 +415,7 @@ function getAvailableFiles(chatHistory) {
367
415
  export {
368
416
  getUniqueId,
369
417
  getSearchResultId,
418
+ extractCitationTitle,
370
419
  convertToSingleContentChatHistory,
371
420
  chatArgsHasImageUrl,
372
421
  chatArgsHasType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aj-archipelago/cortex",
3
- "version": "1.3.62",
3
+ "version": "1.3.63",
4
4
  "description": "Cortex is a GraphQL API for AI. It provides a simple, extensible interface for using AI services from OpenAI, Azure and others.",
5
5
  "private": false,
6
6
  "repository": {
@@ -36,5 +36,6 @@ export default {
36
36
  manageTokenLength: true,
37
37
  // Use this pathway as a tool for LLM calls
38
38
  toolDefinition: {},
39
+ requestLoggingDisabled: false,
39
40
  };
40
41
 
@@ -1,13 +1,66 @@
1
1
  // bing_afagent.js
2
2
  // Web search tool
3
3
 
4
+ import { config } from '../config.js';
5
+ import logger from '../lib/logger.js';
6
+
4
7
  export default {
5
8
  inputParameters: {
6
9
  text: ``,
10
+ tool_choice: 'auto',
11
+ count: 25,
12
+ freshness: 'week',
13
+ market: 'en-us',
14
+ set_lang: 'en'
7
15
  },
8
16
  timeout: 400,
9
17
  enableDuplicateRequests: false,
10
18
  model: 'azure-bing-agent',
11
- useInputChunking: false
19
+ useInputChunking: false,
20
+ instructions: `You are a Bing search agent responding to user queries.
21
+
22
+ Instructions:
23
+ - CRITICAL: Always use your search tools and perform Bing searches before answering
24
+ - Retrieve only the most recent, credible, and relevant results using strict date filters.
25
+ - Exclude or explicitly tag outdated, speculative, forum, sponsored, or low-quality sources (e.g., Reddit, Quora, clickbait sites).
26
+ - Prioritize accuracy, factual precision, and clarity. Deduplicate similar results; show only unique, high-value sources.
27
+
28
+ Response Format:
29
+ - Precise citations are critical - make sure that each topic has a separate paragraph in your response and that each paragraph has direct citations
30
+ - Return the original search results with titles/snippets and direct citations only.
31
+ - Do not include notes, explanations, questions, commentary or any additional output.
32
+
33
+ Your only task is to deliver up-to-date, authoritative, and concise results — nothing else.`,
34
+ parallel_tool_calls: true,
35
+
36
+ executePathway: async ({args, runAllPrompts, resolver}) => {
37
+ // Build dynamic tools configuration based on input parameters
38
+ const azureFoundryBingSearchConnectionId = config.get('azureFoundryBingSearchConnectionId');
39
+ if (azureFoundryBingSearchConnectionId) {
40
+
41
+ const tools = [
42
+ {
43
+ type: "bing_grounding",
44
+ bing_grounding: {
45
+ search_configurations: [
46
+ {
47
+ connection_id: config.get('azureFoundryBingSearchConnectionId'),
48
+ count: args.count || 25,
49
+ freshness: args.freshness || 'week',
50
+ market: args.market || 'en-us',
51
+ set_lang: args.set_lang || 'en'
52
+ }
53
+ ]
54
+ }
55
+ }
56
+ ];
57
+
58
+ // Add tools to the pathway configuration for this execution
59
+ resolver.pathway.tools = tools;
60
+ }
61
+
62
+ // Run the standard pathway execution
63
+ return await runAllPrompts(args);
64
+ }
12
65
  };
13
66
 
@@ -358,8 +358,8 @@ export default {
358
358
  logger.warn(`Some tool calls failed: ${failedTools.map(t => t.error).join(', ')}`);
359
359
  }
360
360
  } else {
361
- // No tool calls, this is the final response
362
- finalResponse = response.content;
361
+ // No tool calls, this is the final response, but it's a CortexResponse object
362
+ finalResponse = response.output_text;
363
363
  }
364
364
  }
365
365
 
@@ -372,7 +372,6 @@ export default {
372
372
  } catch (e) {
373
373
  resolver.logError(e);
374
374
  const chatResponse = await callPathway('sys_generator_quick', {...args, model: styleModel}, resolver);
375
- resolver.tool = JSON.stringify({ search: false, title: args.title });
376
375
  return args.stream ? null : chatResponse;
377
376
  }
378
377
  }
@@ -12,7 +12,7 @@ export default {
12
12
  inputParameters: {
13
13
  chatHistory: [{role: '', content: []}],
14
14
  contextId: ``,
15
+ model: 'oai-gpt4o',
15
16
  },
16
- model: 'oai-gpt4o',
17
17
  useInputChunking: false,
18
18
  }
@@ -0,0 +1,27 @@
1
+ // google_cse.js
2
+ // Google Custom Search pathway
3
+
4
+ export default {
5
+ inputParameters: {
6
+ text: ``,
7
+ q: ``,
8
+ num: 10,
9
+ start: 1,
10
+ safe: 'off', // 'off' | 'active'
11
+ dateRestrict: '',
12
+ siteSearch: '',
13
+ siteSearchFilter: '', // 'e' | 'i'
14
+ searchType: '', // 'image'
15
+ gl: '',
16
+ hl: '',
17
+ lr: '',
18
+ sort: '',
19
+ exactTerms: '',
20
+ excludeTerms: '',
21
+ orTerms: '',
22
+ fileType: ''
23
+ },
24
+ timeout: 400,
25
+ enableDuplicateRequests: false,
26
+ model: 'google-cse',
27
+ };
@@ -0,0 +1,18 @@
1
+ import { Prompt } from '../server/prompt.js';
2
+
3
+ export default {
4
+ prompt:
5
+ [
6
+ new Prompt({ messages: [
7
+ {"role": "system", "content": "You are a helpful AI assistant with live search capabilities across a variety of internet sources including news, web, x, rss, etc. Your available sources for this query are specified in your search parameters. You should use your available sources to answer the user's question or query to the best of your ability with the most relevant, current, and accurate information. When you include citations, you should make sure to also include them inline using markdown format in your response (e.g. [1(https://example.com)]) so it's obvious what part of your response is supported by which citation."},
8
+ {"role": "user", "content": "{{text}}"},
9
+ ]}),
10
+ ],
11
+
12
+ model: 'xai-grok-4',
13
+ useInputChunking: false,
14
+ inputParameters: {
15
+ stream: false,
16
+ search_parameters: ''
17
+ }
18
+ };
@@ -19,4 +19,5 @@ export default {
19
19
  useInputChunking: false,
20
20
  json: true,
21
21
  responseFormat: { type: "json_object" },
22
+ requestLoggingDisabled: true,
22
23
  }
@@ -19,5 +19,6 @@ export default {
19
19
  model: 'oai-gpt41',
20
20
  useInputChunking: false,
21
21
  json: true,
22
+ requestLoggingDisabled: true,
22
23
  ...config.get('entityConstants')
23
24
  }
@@ -29,6 +29,7 @@ export default {
29
29
  model: 'oai-gpt41-mini',
30
30
  useInputChunking: false,
31
31
  enableDuplicateRequests: false,
32
+ requestLoggingDisabled: true,
32
33
  timeout: 300,
33
34
  executePathway: async ({args, runAllPrompts}) => {
34
35
 
@@ -8,6 +8,7 @@ import { config } from '../../../config.js';
8
8
  import { chatArgsHasImageUrl, removeOldImageAndFileContent, getAvailableFiles } from '../../../lib/util.js';
9
9
  import { Prompt } from '../../../server/prompt.js';
10
10
  import { getToolsForEntity, loadEntityConfig } from './tools/shared/sys_entity_tools.js';
11
+ import CortexResponse from '../../../lib/cortexResponse.js';
11
12
 
12
13
  export default {
13
14
  emulateOpenAIChatModel: 'cortex-agent',
@@ -39,7 +40,14 @@ export default {
39
40
  return;
40
41
  }
41
42
 
42
- const { tool_calls } = message;
43
+ // Handle both CortexResponse objects and plain message objects
44
+ let tool_calls;
45
+ if (message instanceof CortexResponse) {
46
+ tool_calls = message.toolCalls || message.functionCall ? [message.functionCall] : null;
47
+ } else {
48
+ tool_calls = message.tool_calls;
49
+ }
50
+
43
51
  const pathwayResolver = resolver;
44
52
  const { entityTools, entityToolsOpenAiFormat } = args;
45
53
 
@@ -235,6 +243,33 @@ export default {
235
243
  args.chatHistory = [];
236
244
  }
237
245
 
246
+ if(entityConfig?.files && entityConfig?.files.length > 0) {
247
+ //get last user message if not create one to add files to
248
+ let lastUserMessage = args.chatHistory.filter(message => message.role === "user").slice(-1)[0];
249
+ if(!lastUserMessage) {
250
+ lastUserMessage = {
251
+ role: "user",
252
+ content: []
253
+ };
254
+ args.chatHistory.push(lastUserMessage);
255
+ }
256
+
257
+ //if last user message content is not array then convert to array
258
+ if(!Array.isArray(lastUserMessage.content)) {
259
+ lastUserMessage.content = lastUserMessage.content ? [lastUserMessage.content] : [];
260
+ }
261
+
262
+ //add files to the last user message content
263
+ lastUserMessage.content.push(...entityConfig?.files.map(file => ({
264
+ type: "image_url",
265
+ gcs: file?.gcs,
266
+ url: file?.url,
267
+ image_url: { url: file?.url },
268
+ originalFilename: file?.name
269
+ })
270
+ ));
271
+ }
272
+
238
273
  // Kick off the memory lookup required pathway in parallel - this takes like 500ms so we want to start it early
239
274
  let memoryLookupRequiredPromise = null;
240
275
  if (entityUseMemory) {
@@ -284,8 +319,19 @@ export default {
284
319
  ];
285
320
 
286
321
  // set the style model if applicable
287
- const { aiStyle, AI_STYLE_ANTHROPIC, AI_STYLE_OPENAI } = args;
288
- const styleModel = aiStyle === "Anthropic" ? AI_STYLE_ANTHROPIC : AI_STYLE_OPENAI;
322
+ const { aiStyle, AI_STYLE_ANTHROPIC, AI_STYLE_OPENAI, AI_STYLE_ANTHROPIC_RESEARCH, AI_STYLE_OPENAI_RESEARCH, AI_STYLE_XAI, AI_STYLE_XAI_RESEARCH, AI_STYLE_GOOGLE, AI_STYLE_GOOGLE_RESEARCH } = args;
323
+
324
+ // Create a mapping of AI styles to their corresponding models
325
+ const styleModelMap = {
326
+ "Anthropic": { normal: AI_STYLE_ANTHROPIC, research: AI_STYLE_ANTHROPIC_RESEARCH },
327
+ "OpenAI": { normal: AI_STYLE_OPENAI, research: AI_STYLE_OPENAI_RESEARCH },
328
+ "XAI": { normal: AI_STYLE_XAI, research: AI_STYLE_XAI_RESEARCH },
329
+ "Google": { normal: AI_STYLE_GOOGLE, research: AI_STYLE_GOOGLE_RESEARCH }
330
+ };
331
+
332
+ // Get the appropriate model based on AI style and research mode
333
+ const styleConfig = styleModelMap[aiStyle] || styleModelMap["OpenAI"]; // Default to OpenAI
334
+ const styleModel = researchMode ? styleConfig.research : styleConfig.normal;
289
335
 
290
336
  // Limit the chat history to 20 messages to speed up processing
291
337
  if (args.messages && args.messages.length > 0) {
@@ -328,6 +374,7 @@ export default {
328
374
 
329
375
  let response = await runAllPrompts({
330
376
  ...args,
377
+ modelOverride: styleModel,
331
378
  chatHistory: currentMessages,
332
379
  availableFiles,
333
380
  tools: entityToolsOpenAiFormat,
@@ -335,7 +382,12 @@ export default {
335
382
  });
336
383
 
337
384
  let toolCallback = pathwayResolver.pathway.toolCallback;
338
- while (response?.tool_calls) {
385
+
386
+ // Handle both CortexResponse objects and plain responses
387
+ while (response && (
388
+ (response instanceof CortexResponse && response.hasToolCalls()) ||
389
+ (typeof response === 'object' && response.tool_calls)
390
+ )) {
339
391
  response = await toolCallback(args, response, pathwayResolver);
340
392
  }
341
393
 
@@ -7,6 +7,7 @@ export default {
7
7
  contextId: ``,
8
8
  aiName: "Jarvis",
9
9
  language: "English",
10
+ model: 'oai-gpt41-mini',
10
11
  },
11
12
  useInputChunking: false,
12
13
  enableDuplicateRequests: false,
@@ -24,6 +24,22 @@ export default {
24
24
  userMessage: {
25
25
  type: "string",
26
26
  description: "A user-friendly message that describes what you're doing with this tool"
27
+ },
28
+ count: {
29
+ type: "integer",
30
+ description: "Number of search results to return (default: 25, minimum: 1, maximum: 50)",
31
+ },
32
+ freshness: {
33
+ type: "string",
34
+ description: "Time filter for search results in Bing freshness format (e.g., 'day', 'week', 'month', 'year', or an explicit date range 'YYYY-MM-DD..YYYY-MM-DD' or single date 'YYYY-MM-DD')",
35
+ },
36
+ market: {
37
+ type: "string",
38
+ description: "Market/locale for search results (e.g., 'en-us', 'en-gb')",
39
+ },
40
+ set_lang: {
41
+ type: "string",
42
+ description: "Language for search results (e.g., 'en', 'es', 'fr')",
27
43
  }
28
44
  },
29
45
  required: ["text", "userMessage"]
@@ -43,8 +59,18 @@ export default {
43
59
  // Call the Bing search pathway
44
60
  //remove model from args as bing_afagent has model in its own
45
61
  const { model, ...restArgs } = args;
62
+
63
+ // Extract search parameters and pass them through
64
+ const searchParams = {};
65
+ if (args.count !== undefined) searchParams.count = args.count;
66
+ if (args.freshness !== undefined) searchParams.freshness = args.freshness;
67
+ if (args.market !== undefined) searchParams.market = args.market;
68
+ if (args.set_lang !== undefined) searchParams.set_lang = args.set_lang;
69
+
46
70
  const rawResponse = await callPathway('bing_afagent', {
71
+ tool_choice: 'auto',
47
72
  ...restArgs,
73
+ ...searchParams
48
74
  }, resolver);
49
75
 
50
76
  // Add error handling for malformed JSON