@peopl-health/nexus 3.8.0 → 3.8.2

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.
@@ -1,6 +1,5 @@
1
1
  const { OpenAI } = require('openai');
2
2
 
3
- const { Config_ID } = require('../config/airtableConfig');
4
3
  const { getCurrentMexicoDateTime } = require('../utils/dateUtils');
5
4
  const { retryWithBackoff } = require('../utils/retryUtils');
6
5
  const { logger } = require('../utils/logger');
@@ -8,7 +7,7 @@ const { Thread } = require('../models/threadModel');
8
7
  const { DefaultMemoryManager } = require('../memory/DefaultMemoryManager');
9
8
  const { OpenAIResponsesProvider } = require('../providers/OpenAIResponsesProvider');
10
9
  const { handleFunctionCalls } = require('../providers/OpenAIResponsesProviderTools');
11
- const { getRecordByFilter } = require('../services/airtableService');
10
+ const { composePrompt } = require('../services/promptComposerService');
12
11
  const { getAssistantById } = require('../services/assistantResolver');
13
12
 
14
13
  const MAX_FUNCTION_ROUNDS = parseInt(process.env.MAX_FUNCTION_ROUNDS || '5', 10);
@@ -108,12 +107,15 @@ class EvalProvider {
108
107
  async _resolvePrompt(fallbackPrompt, assistantId, thread, promptVariables) {
109
108
  let devContent;
110
109
  if (this.promptSource === 'airtable' && assistantId) {
111
- const devRecord = await getRecordByFilter(Config_ID, 'responses', `{prompt_id} = "${assistantId}"`);
112
- devContent = devRecord?.[0]?.content || '';
110
+ const { resolvedPrompt } = await composePrompt({
111
+ promptId: assistantId,
112
+ variables: promptVariables,
113
+ });
114
+ devContent = resolvedPrompt;
113
115
  } else {
114
116
  devContent = fallbackPrompt;
117
+ devContent = devContent.replace(/\{\{(\w+)\}\}/g, (_, key) => promptVariables[key] ?? '');
115
118
  }
116
- devContent = devContent.replace(/\{\{(\w+)\}\}/g, (_, key) => promptVariables[key] ?? '');
117
119
 
118
120
  let assistant = null;
119
121
  let toolSchemas = [];
@@ -1,7 +1,5 @@
1
1
  const { OpenAI } = require('openai');
2
2
 
3
- const { Config_ID } = require('../config/airtableConfig');
4
-
5
3
  const { retryWithBackoff } = require('../utils/retryUtils');
6
4
  const { logger } = require('../utils/logger');
7
5
  const { getCurrentMexicoDateTime } = require('../utils/dateUtils');
@@ -10,7 +8,7 @@ const { DefaultMemoryManager } = require('../memory/DefaultMemoryManager');
10
8
 
11
9
  const { getLastNMessages } = require('../helpers/messageHelper');
12
10
 
13
- const { getRecordByFilter } = require('../services/airtableService');
11
+ const { composePrompt, resolveTools } = require('../services/promptComposerService');
14
12
  const { handleFunctionCalls } = require('./OpenAIResponsesProviderTools');
15
13
 
16
14
  const CONVERSATION_PREFIX = 'conv_';
@@ -314,12 +312,16 @@ class OpenAIResponsesProvider {
314
312
  accumulatedUsage.total_tokens += usage.total_tokens || 0;
315
313
  };
316
314
 
317
- const devRecord = await getRecordByFilter(Config_ID, 'responses', `{prompt_id} = "${assistantId}"`);
318
- let devContent = devRecord?.[0]?.content || '';
319
- if (promptVariables) devContent = devContent.replace(/\{\{(\w+)\}\}/g, (_, key) => promptVariables[key] ?? '');
315
+ const { resolvedPrompt, snippetIds } = await composePrompt({
316
+ promptId: assistantId,
317
+ variables: promptVariables,
318
+ });
320
319
 
320
+ const { toolIds, filtered } = await resolveTools({ promptId: assistantId, assistant });
321
+ let devContent = resolvedPrompt;
321
322
  if (assistant?.tools?.size) {
322
- const toolNames = Array.from(assistant.tools.keys()).join(', ');
323
+ const activeToolNames = filtered ? toolIds : Array.from(assistant.tools.keys());
324
+ const toolNames = activeToolNames.join(', ');
323
325
  devContent += `\n\nYou only have access to these tools: ${toolNames}. Do not call or reference any tools not listed here.`;
324
326
  }
325
327
 
@@ -376,6 +378,9 @@ class OpenAIResponsesProvider {
376
378
  tools_executed: allToolsExecuted,
377
379
  retries: totalRetries,
378
380
  usage: accumulatedUsage,
381
+ resolved_prompt: devContent,
382
+ snippet_ids: snippetIds,
383
+ tool_ids: toolIds,
379
384
  };
380
385
  }
381
386
 
@@ -243,6 +243,9 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
243
243
  const response_id = runResult.run?.id || null;
244
244
  const usage = runResult.run?.usage || null;
245
245
  const model = runResult.run?.model || null;
246
+ const resolved_prompt = runResult.run?.resolved_prompt || null;
247
+ const snippet_ids = runResult.run?.snippet_ids || [];
248
+ const tool_ids = runResult.run?.tool_ids || [];
246
249
 
247
250
  const output = sanitizeOutput(rawOutput);
248
251
  if (rawOutput !== output) {
@@ -294,6 +297,9 @@ const replyAssistantCore = async (code, message_ = null, thread_ = null, runOpti
294
297
  prompt_config: prompt || null,
295
298
  response_id: response_id || null,
296
299
  context_message_count: lastMessage?.length || null,
300
+ resolved_prompt: resolved_prompt,
301
+ snippet_ids: snippet_ids,
302
+ tool_ids: tool_ids,
297
303
  }).catch(err => logger.error('[replyAssistant] Failed to store metrics', { error: err.message }));
298
304
 
299
305
  const alertThreshold = parseInt(process.env.TOKEN_ALERT_THRESHOLD, 10);
@@ -10,6 +10,7 @@ const CACHE_TTL = 5 * 60 * 1000;
10
10
 
11
11
  const promptCache = new MapCache({ maxSize: 50, ttl: CACHE_TTL });
12
12
  const snippetCache = new MapCache({ maxSize: 200, ttl: CACHE_TTL });
13
+ const toolCache = new MapCache({ maxSize: 50, ttl: CACHE_TTL });
13
14
 
14
15
  async function fetchBasePrompt(promptId) {
15
16
  const cacheKey = `prompt:${promptId}`;
@@ -86,9 +87,66 @@ async function composePrompt({ promptId, variables = null, status = null }) {
86
87
  return { resolvedPrompt, snippetIds };
87
88
  }
88
89
 
90
+ async function fetchToolMapping(promptId) {
91
+ const cacheKey = `tools:${promptId}`;
92
+ const cached = toolCache.get(cacheKey);
93
+ if (cached) return cached;
94
+
95
+ try {
96
+ const records = await getRecordByFilter(
97
+ Config_ID,
98
+ 'tools',
99
+ `FIND("${promptId}", ARRAYJOIN({prompts}))`,
100
+ );
101
+ const tools = records || [];
102
+ toolCache.set(cacheKey, tools);
103
+ return tools;
104
+ } catch (error) {
105
+ logger.warn('[promptComposer] Failed to fetch tool mapping, using all registered tools', {
106
+ promptId, error: error.message,
107
+ });
108
+ return [];
109
+ }
110
+ }
111
+
112
+ async function resolveTools({ promptId, assistant, status = null }) {
113
+ if (!assistant?.tools?.size) return { toolIds: [], filtered: false };
114
+
115
+ const mappedTools = await fetchToolMapping(promptId);
116
+ if (!mappedTools.length) return { toolIds: [], filtered: false };
117
+
118
+ const activeTools = status
119
+ ? mappedTools.filter(t => !t.status || t.status === status)
120
+ : mappedTools;
121
+
122
+ const registeredNames = new Set(assistant.tools.keys());
123
+ const validToolIds = [];
124
+
125
+ for (const tool of activeTools) {
126
+ const id = tool.tool_id;
127
+ if (registeredNames.has(id)) {
128
+ validToolIds.push(id);
129
+ } else {
130
+ logger.warn('[promptComposer] Tool mapped in Airtable but not registered in code', {
131
+ toolId: id, promptId,
132
+ });
133
+ }
134
+ }
135
+
136
+ logger.debug('[promptComposer] Tools resolved', {
137
+ promptId,
138
+ mapped: activeTools.length,
139
+ valid: validToolIds.length,
140
+ filtered: true,
141
+ });
142
+
143
+ return { toolIds: validToolIds, filtered: true };
144
+ }
145
+
89
146
  function clearCache() {
90
147
  promptCache.clear();
91
148
  snippetCache.clear();
149
+ toolCache.clear();
92
150
  }
93
151
 
94
- module.exports = { composePrompt, clearCache, _test: { applyVariables, sortSnippets } };
152
+ module.exports = { composePrompt, resolveTools, clearCache, _test: { applyVariables, sortSnippets } };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "3.8.0",
3
+ "version": "3.8.2",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",