Created
May 21, 2025 10:25
-
-
Save jiggyjo11/4328127b82f40a58f40e9ac6a44bf3df to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Action, | |
IAgentRuntime, | |
logger, | |
Memory, | |
State, | |
HandlerCallback, | |
ModelType, | |
} from '@elizaos/core'; | |
const quantitativePromptTemplate = `# Task: Extract and validate numeric data from the following text. | |
Text: {{text}} | |
# Instructions: | |
1. Identify any numbers with units (mg, g, kg, ml, l, μl, μM, mM, M, $) | |
2. For each number found, list it in markdown format: | |
- Value: [number] | |
- Unit: [unit] | |
- Type: [concentration/volume/weight/cost] | |
3. Only extract numbers that are explicitly stated | |
4. Do not make assumptions or calculations | |
# Response Format: | |
Return a markdown list. Each item should be formatted like this: | |
- Value: 100 | |
Unit: μM | |
Type: concentration | |
- Value: 5 | |
Unit: ml | |
Type: volume | |
Example valid response: | |
- Value: 100 | |
Unit: μM | |
Type: concentration | |
- Value: 5 | |
Unit: ml | |
Type: volume`; | |
export const quantitativeActions = { | |
DETECT_QUANTITATIVE: { | |
name: 'DETECT_QUANTITATIVE', | |
similes: ['PROCESS_NUMBERS', 'ANALYZE_MEASUREMENTS', 'EXTRACT_QUANTITIES'], | |
description: | |
'Detects and processes numeric content in messages, including measurements and quantities', | |
validate: async (runtime: IAgentRuntime, message: Memory, state: State): Promise<boolean> => { | |
logger.warn('=== DETECT_QUANTITATIVE VALIDATE START ==='); | |
try { | |
const text = message.content.text?.toLowerCase() || ''; | |
logger.warn('Validate received text:', { text }); | |
// Check if we've already processed this message | |
const messageId = message.id; | |
if (state.data?.processedMessages?.includes(messageId)) { | |
logger.warn('Message already processed', { messageId }); | |
return false; | |
} | |
// Filter out Discord mentions in the format (@1368906455100559415) | |
const filteredText = text.replace(/\(@\d+\)/g, ''); | |
// Simple check for numbers and units | |
const hasNumeric = /\d+(?:\s*(?:mg|g|kg|ml|l|μl|μM|mM|M|\$))?/i.test(filteredText); | |
logger.warn('Numeric content detection result', { hasNumeric }); | |
return hasNumeric; | |
} catch (error) { | |
logger.error('Error validating quantitative content:', error); | |
return false; | |
} | |
}, | |
handler: async ( | |
runtime: IAgentRuntime, | |
message: Memory, | |
state: State, | |
_options: any, | |
callback: HandlerCallback | |
): Promise<void> => { | |
logger.warn('=== DETECT_QUANTITATIVE HANDLER START ==='); | |
try { | |
const text = message.content.text || ''; | |
logger.warn('Handler received text:', { text }); | |
// Use the prompt template to extract numeric data | |
const prompt = quantitativePromptTemplate.replace('{{text}}', text); | |
logger.warn('Using prompt:', { prompt }); | |
const model = runtime.getModel(ModelType.TEXT_LARGE); | |
if (!model) { | |
logger.error('Model not found'); | |
throw new Error('Text generation model not found'); | |
} | |
logger.warn('Calling model...'); | |
let response; | |
try { | |
response = await model(runtime, { prompt }); | |
logger.warn('Model call completed'); | |
} catch (error) { | |
logger.error('Error calling model:', error); | |
throw error; | |
} | |
if (!response) { | |
logger.error('Model returned empty response'); | |
throw new Error('Model returned empty response'); | |
} | |
// Log the raw response | |
logger.warn('Raw model response:', { response }); | |
// Parse the markdown response | |
const numericData = []; | |
const lines = response.split('\n'); | |
let currentItem = null; | |
for (const line of lines) { | |
const trimmedLine = line.trim(); | |
if (trimmedLine.startsWith('- Value:')) { | |
if (currentItem) { | |
numericData.push(currentItem); | |
} | |
currentItem = { value: null, unit: null, type: null }; | |
const valueMatch = trimmedLine.match(/Value:\s*([\d.]+)/); | |
if (valueMatch) { | |
currentItem.value = parseFloat(valueMatch[1]); | |
} | |
} else if (trimmedLine.startsWith('Unit:')) { | |
if (currentItem) { | |
const unitMatch = trimmedLine.match(/Unit:\s*([^\n]+)/); | |
if (unitMatch) { | |
currentItem.unit = unitMatch[1].trim(); | |
} | |
} | |
} else if (trimmedLine.startsWith('Type:')) { | |
if (currentItem) { | |
const typeMatch = trimmedLine.match(/Type:\s*([^\n]+)/); | |
if (typeMatch) { | |
currentItem.type = typeMatch[1].trim(); | |
} | |
} | |
} | |
} | |
// Add the last item if exists | |
if (currentItem) { | |
numericData.push(currentItem); | |
} | |
// Filter out incomplete items | |
const validNumericData = numericData.filter( | |
(item) => item.value !== null && item.unit !== null && item.type !== null | |
); | |
logger.warn('Parsed numeric data:', { validNumericData }); | |
// Create memory of processing | |
await runtime.createMemory( | |
{ | |
entityId: runtime.agentId, | |
agentId: runtime.agentId, | |
roomId: message.roomId, | |
content: { | |
text: message.content.text, | |
actions: ['DETECT_QUANTITATIVE'], | |
metadata: { | |
numericData: validNumericData, | |
hasValidData: validNumericData.length > 0, | |
}, | |
}, | |
createdAt: Date.now(), | |
}, | |
'messages' | |
); | |
// Pass results through callback | |
await callback({ | |
actions: ['DETECT_QUANTITATIVE'], | |
metadata: { | |
numericData: validNumericData, | |
hasValidData: validNumericData.length > 0, | |
}, | |
}); | |
logger.warn('Numeric content processing complete', { | |
dataCount: validNumericData.length, | |
}); | |
// Mark message as processed | |
if (!state.data.processedMessages) { | |
state.data.processedMessages = []; | |
} | |
state.data.processedMessages.push(message.id); | |
} catch (error) { | |
logger.error('Error processing numeric content:', error); | |
await runtime.createMemory( | |
{ | |
entityId: runtime.agentId, | |
agentId: runtime.agentId, | |
roomId: message.roomId, | |
content: { | |
text: message.content.text, | |
actions: ['DETECT_QUANTITATIVE'], | |
metadata: { | |
error: error.message, | |
}, | |
}, | |
createdAt: Date.now(), | |
}, | |
'messages' | |
); | |
await callback({ | |
actions: ['DETECT_QUANTITATIVE'], | |
metadata: { | |
error: error.message, | |
}, | |
}); | |
} | |
}, | |
examples: [ | |
[ | |
{ | |
name: '{{name1}}', | |
content: { | |
text: 'I need 5mg of substance X', | |
source: 'discord', | |
}, | |
}, | |
{ | |
name: '{{name2}}', | |
content: { | |
text: 'Processed quantity: 5mg', | |
actions: ['DETECT_QUANTITATIVE'], | |
metadata: { | |
numericData: [{ value: 5, unit: 'mg', type: 'weight' }], | |
hasValidData: true, | |
}, | |
}, | |
}, | |
], | |
[ | |
{ | |
name: '{{name1}}', | |
content: { | |
text: 'The concentration is 100μM', | |
source: 'discord', | |
}, | |
}, | |
{ | |
name: '{{name2}}', | |
content: { | |
text: 'Processed concentration: 100μM', | |
actions: ['DETECT_QUANTITATIVE'], | |
metadata: { | |
numericData: [{ value: 100, unit: 'μM', type: 'concentration' }], | |
hasValidData: true, | |
}, | |
}, | |
}, | |
], | |
], | |
}, | |
}; | |
export default quantitativeActions; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment