@neurameter/core 0.1.3 → 0.1.4
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/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
'use strict';var m={"gpt-4o":128e3,"gpt-4o-mini":128e3,"gpt-4.1":1e6,"gpt-4.1-mini":1e6,o1:2e5,"o3-mini":2e5,"claude-sonnet-4-20250514":2e5,"claude-haiku-4-5-20251001":2e5,"claude-opus-4-20250514":2e5,"claude-sonnet-4":2e5,"claude-haiku-4":2e5,"claude-opus-4":2e5};function _(t){if(typeof t!="string"){let e=typeof t=="object"?JSON.stringify(t):String(t??"");return Math.ceil(e.length/4)}return Math.ceil(t.length/4)}function f(t,e){let r=x(e),o=0,n=0,i=0;for(let a of t){let u=_(a.content);a.role==="system"?o+=u:a.role==="tool"?i+=u:n+=u;}let s=o+n+i;return {estimatedInputTokens:s,modelContextLimit:r,utilizationPercent:r>0?s/r:0,messageCount:t.length,systemPromptTokens:o,conversationTokens:n,toolResultTokens:i}}function x(t){if(m[t]!==void 0)return m[t];for(let[e,r]of Object.entries(m))if(t.startsWith(e))return r;return 128e3}function c(t,e){let r=0,o=t.inputTokens-(t.cachedTokens??0);if(o>0&&(r+=h(o*e.inputPricePerMToken,1e6)),t.cachedTokens&&t.cachedTokens>0){let n=e.cachedInputPricePerMToken??e.inputPricePerMToken;r+=h(t.cachedTokens*n,1e6);}if(t.outputTokens>0&&(r+=h(t.outputTokens*e.outputPricePerMToken,1e6)),t.reasoningTokens&&t.reasoningTokens>0){let n=e.reasoningPricePerMToken??e.outputPricePerMToken;r+=h(t.reasoningTokens*n,1e6);}return r}function h(t,e){let r=Math.trunc(t/e);return (t-r*e)*2>=e?r+1:r}var M={openai:{"gpt-4o":{inputPricePerMToken:25e5,outputPricePerMToken:1e7,cachedInputPricePerMToken:125e4},"gpt-4o-mini":{inputPricePerMToken:15e4,outputPricePerMToken:6e5,cachedInputPricePerMToken:75e3},"gpt-4.1":{inputPricePerMToken:2e6,outputPricePerMToken:8e6,cachedInputPricePerMToken:5e5},"gpt-4.1-mini":{inputPricePerMToken:4e5,outputPricePerMToken:16e5,cachedInputPricePerMToken:1e5},o1:{inputPricePerMToken:15e6,outputPricePerMToken:6e7,reasoningPricePerMToken:6e7,cachedInputPricePerMToken:75e5},"o3-mini":{inputPricePerMToken:11e5,outputPricePerMToken:44e5,reasoningPricePerMToken:44e5,cachedInputPricePerMToken:55e4}},anthropic:{"claude-sonnet-4-20250514":{inputPricePerMToken:3e6,outputPricePerMToken:15e6,cachedInputPricePerMToken:3e5},"claude-haiku-4-5-20251001":{inputPricePerMToken:8e5,outputPricePerMToken:4e6,cachedInputPricePerMToken:8e4},"claude-opus-4-20250514":{inputPricePerMToken:15e6,outputPricePerMToken:75e6,cachedInputPricePerMToken:15e5}}};function l(t,e){
|
|
1
|
+
'use strict';var m={"gpt-4o":128e3,"gpt-4o-mini":128e3,"gpt-4.1":1e6,"gpt-4.1-mini":1e6,o1:2e5,"o3-mini":2e5,"claude-sonnet-4-20250514":2e5,"claude-haiku-4-5-20251001":2e5,"claude-opus-4-20250514":2e5,"claude-sonnet-4":2e5,"claude-haiku-4":2e5,"claude-opus-4":2e5};function _(t){if(typeof t!="string"){let e=typeof t=="object"?JSON.stringify(t):String(t??"");return Math.ceil(e.length/4)}return Math.ceil(t.length/4)}function f(t,e){let r=x(e),o=0,n=0,i=0;for(let a of t){let u=_(a.content);a.role==="system"?o+=u:a.role==="tool"?i+=u:n+=u;}let s=o+n+i;return {estimatedInputTokens:s,modelContextLimit:r,utilizationPercent:r>0?s/r:0,messageCount:t.length,systemPromptTokens:o,conversationTokens:n,toolResultTokens:i}}function x(t){if(m[t]!==void 0)return m[t];for(let[e,r]of Object.entries(m))if(t.startsWith(e))return r;return 128e3}function c(t,e){let r=0,o=t.inputTokens-(t.cachedTokens??0);if(o>0&&(r+=h(o*e.inputPricePerMToken,1e6)),t.cachedTokens&&t.cachedTokens>0){let n=e.cachedInputPricePerMToken??e.inputPricePerMToken;r+=h(t.cachedTokens*n,1e6);}if(t.outputTokens>0&&(r+=h(t.outputTokens*e.outputPricePerMToken,1e6)),t.reasoningTokens&&t.reasoningTokens>0){let n=e.reasoningPricePerMToken??e.outputPricePerMToken;r+=h(t.reasoningTokens*n,1e6);}return r}function h(t,e){let r=Math.trunc(t/e);return (t-r*e)*2>=e?r+1:r}var M={openai:{"gpt-4o":{inputPricePerMToken:25e5,outputPricePerMToken:1e7,cachedInputPricePerMToken:125e4},"gpt-4o-mini":{inputPricePerMToken:15e4,outputPricePerMToken:6e5,cachedInputPricePerMToken:75e3},"gpt-4.1":{inputPricePerMToken:2e6,outputPricePerMToken:8e6,cachedInputPricePerMToken:5e5},"gpt-4.1-mini":{inputPricePerMToken:4e5,outputPricePerMToken:16e5,cachedInputPricePerMToken:1e5},o1:{inputPricePerMToken:15e6,outputPricePerMToken:6e7,reasoningPricePerMToken:6e7,cachedInputPricePerMToken:75e5},"o3-mini":{inputPricePerMToken:11e5,outputPricePerMToken:44e5,reasoningPricePerMToken:44e5,cachedInputPricePerMToken:55e4}},anthropic:{"claude-sonnet-4-20250514":{inputPricePerMToken:3e6,outputPricePerMToken:15e6,cachedInputPricePerMToken:3e5},"claude-haiku-4-5-20251001":{inputPricePerMToken:8e5,outputPricePerMToken:4e6,cachedInputPricePerMToken:8e4},"claude-opus-4-20250514":{inputPricePerMToken:15e6,outputPricePerMToken:75e6,cachedInputPricePerMToken:15e5}}};function l(t,e){let r=M[t];if(r){if(r[e])return r[e];for(let o of Object.keys(r))if(e.startsWith(o))return r[o]}}var d=class extends Error{rule;current;threshold;suggestion;constructor(e,r){super(`NeuraMeter guard: ${e.ruleType} exceeded (${e.currentValue} > ${e.threshold})`),this.name="NeuraMeterGuardError",this.rule=e.ruleType,this.current=e.currentValue,this.threshold=e.threshold,this.suggestion=r;}};function k(t,e,r){let o=t.mode??"notify",n=[],i=f(e.messages,e.model);if(t.maxInputTokens&&i.estimatedInputTokens>t.maxInputTokens&&n.push({ruleType:"input_tokens",currentValue:i.estimatedInputTokens,threshold:t.maxInputTokens,isHard:false}),t.maxInputTokensHard&&i.estimatedInputTokens>t.maxInputTokensHard&&n.push({ruleType:"input_tokens",currentValue:i.estimatedInputTokens,threshold:t.maxInputTokensHard,isHard:true}),t.maxContextUtilization&&i.utilizationPercent>t.maxContextUtilization&&n.push({ruleType:"context_utilization",currentValue:i.utilizationPercent,threshold:t.maxContextUtilization,isHard:false}),t.maxContextUtilizationHard&&i.utilizationPercent>t.maxContextUtilizationHard&&n.push({ruleType:"context_utilization",currentValue:i.utilizationPercent,threshold:t.maxContextUtilizationHard,isHard:true}),t.maxCostPerCall||t.maxCostPerCallHard){let u=l(e.provider,e.model);if(u){let g=c({inputTokens:i.estimatedInputTokens,outputTokens:0},u)/1e6;t.maxCostPerCall&&g>t.maxCostPerCall&&n.push({ruleType:"cost_per_call",currentValue:g,threshold:t.maxCostPerCall,isHard:false}),t.maxCostPerCallHard&&g>t.maxCostPerCallHard&&n.push({ruleType:"cost_per_call",currentValue:g,threshold:t.maxCostPerCallHard,isHard:true});}}t.maxCostPerHour&&r!==void 0&&r>t.maxCostPerHour&&n.push({ruleType:"cost_per_hour",currentValue:r,threshold:t.maxCostPerHour,isHard:o==="block"});let s=C(n,i);if(n.length===0)return {decision:"allow",triggeredRules:n,contextAnalysis:i,suggestion:s};let a=n.some(u=>u.isHard);switch(o){case "notify":return {decision:"notify",triggeredRules:n,contextAnalysis:i,suggestion:s};case "block":return a?{decision:"block",triggeredRules:n,contextAnalysis:i,suggestion:s}:{decision:"notify",triggeredRules:n,contextAnalysis:i,suggestion:s};case "auto-optimize":return {decision:"notify",triggeredRules:n,contextAnalysis:i,suggestion:s};default:return {decision:"allow",triggeredRules:n,contextAnalysis:i,suggestion:s}}}function C(t,e){if(t.length===0)return "";let r=[];for(let o of t)switch(o.ruleType){case "context_utilization":if(e.conversationTokens>e.systemPromptTokens){let n=Math.round(e.conversationTokens/e.estimatedInputTokens*100);r.push(`Summarize conversation history to save ~${n}% of input tokens`);}break;case "input_tokens":r.push(`Reduce input tokens from ${e.estimatedInputTokens.toLocaleString()} to under ${o.threshold.toLocaleString()}`);break;case "cost_per_call":r.push("Consider using a cheaper model (e.g., gpt-4o-mini or claude-haiku)");break;case "cost_per_hour":r.push(`Hourly cost limit exceeded ($${o.currentValue.toFixed(2)} > $${o.threshold.toFixed(2)}). Throttle or pause agent calls`);break}return r.join(". ")||"Review guard thresholds or optimize context usage"}var p=class{traceId;agentName;customerId;taskName;tags;recordFn;constructor(e,r){this.traceId=crypto.randomUUID(),this.agentName=e.agentName,this.customerId=e.customerId,this.taskName=e.taskName,this.tags=e.tags,this.recordFn=r;}span(e){let r=crypto.randomUUID(),o=l(e.provider,e.model),n=o?c(e.usage,o):0;return this.recordFn({traceId:this.traceId,spanId:r,parentSpanId:e.parentSpanId,agentName:e.agentName??this.agentName,taskName:e.taskName??this.taskName,customerId:this.customerId,provider:e.provider,model:e.model,inputTokens:e.usage.inputTokens,outputTokens:e.usage.outputTokens,reasoningTokens:e.usage.reasoningTokens,cachedTokens:e.usage.cachedTokens,costMicrodollars:n,latencyMs:e.latencyMs,tags:{...this.tags,...e.tags}}),r}};function P(t,e){switch(t){case "context_utilization":return `${e.toFixed(1)}%`;case "cost_per_call":case "cost_per_hour":case "budget":return `$${e.toFixed(4)}`;case "input_tokens":return e.toLocaleString();default:return String(e)}}function b(t){return t.split("_").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}function v(t){let e=[{type:"header",text:{type:"plain_text",text:":warning: NeuraMeter Guard Alert",emoji:true}},{type:"section",fields:[{type:"mrkdwn",text:`*Agent:*
|
|
2
2
|
${t.agentName}`},{type:"mrkdwn",text:`*Rule Triggered:*
|
|
3
3
|
${b(t.ruleType)}`},{type:"mrkdwn",text:`*Current Value:*
|
|
4
4
|
${P(t.ruleType,t.currentValue)}`},{type:"mrkdwn",text:`*Threshold:*
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.ts","../src/cost.ts","../src/pricing.ts","../src/guards.ts","../src/trace.ts","../src/slack.ts","../src/meter.ts"],"names":["MODEL_CONTEXT_LIMITS","estimateTokens","content","str","analyzeContext","messages","model","modelLimit","getModelContextLimit","systemTokens","conversationTokens","toolResultTokens","msg","tokens","total","key","limit","calculateCostMicrodollars","usage","pricing","totalMicrodollars","effectiveInputTokens","integerDivRound","cachedPrice","reasoningPrice","numerator","denominator","quotient","PRICING","getModelPricing","provider","NeuraMeterGuardError","rule","suggestion","checkGuards","config","params","hourlyCostDollars","mode","triggeredRules","contextAnalysis","estimatedDollars","generateSuggestion","hasHardViolation","r","rules","ctx","parts","savingPct","Trace","opts","recordFn","spanId","costMicrodollars","formatValue","ruleType","value","formatRuleType","word","buildSlackPayload","message","blocks","sendSlackNotification","webhookUrl","payload","DEFAULT_BATCH_SIZE","DEFAULT_FLUSH_INTERVAL_MS","NeuraMeter","event","agentName","oneHourAgo","recent","e","entries","result","hardRule","primaryRule","optimizeEvent","tokensBefore","costBefore","fullEvent","batch","guardBatch"],"mappings":"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","file":"index.cjs","sourcesContent":["export interface ContextAnalysis {\n estimatedInputTokens: number;\n modelContextLimit: number;\n utilizationPercent: number;\n messageCount: number;\n systemPromptTokens: number;\n conversationTokens: number;\n toolResultTokens: number;\n}\n\nexport interface Message {\n role: string;\n content: string | unknown;\n}\n\n/**\n * Model context window limits (tokens).\n */\nexport const MODEL_CONTEXT_LIMITS: Record<string, number> = {\n 'gpt-4o': 128_000,\n 'gpt-4o-mini': 128_000,\n 'gpt-4.1': 1_000_000,\n 'gpt-4.1-mini': 1_000_000,\n 'o1': 200_000,\n 'o3-mini': 200_000,\n 'claude-sonnet-4-20250514': 200_000,\n 'claude-haiku-4-5-20251001': 200_000,\n 'claude-opus-4-20250514': 200_000,\n // Aliases for prefix matching\n 'claude-sonnet-4': 200_000,\n 'claude-haiku-4': 200_000,\n 'claude-opus-4': 200_000,\n};\n\n/**\n * Estimate token count from a string.\n * Uses a simple heuristic: ~4 chars per token for English,\n * ~1.5 chars per token for CJK/mixed content.\n * This is intentionally fast (<1ms) for SDK use.\n */\nexport function estimateTokens(content: string | unknown): number {\n if (typeof content !== 'string') {\n // For non-string content (e.g., tool_use blocks), serialize and estimate\n const str = typeof content === 'object' ? JSON.stringify(content) : String(content ?? '');\n return Math.ceil(str.length / 4);\n }\n // Simple heuristic: average of ~4 chars/token\n return Math.ceil(content.length / 4);\n}\n\n/**\n * Analyze context window utilization for a set of messages.\n */\nexport function analyzeContext(messages: Message[], model: string): ContextAnalysis {\n const modelLimit = getModelContextLimit(model);\n let systemTokens = 0;\n let conversationTokens = 0;\n let toolResultTokens = 0;\n\n for (const msg of messages) {\n const tokens = estimateTokens(msg.content);\n if (msg.role === 'system') {\n systemTokens += tokens;\n } else if (msg.role === 'tool') {\n toolResultTokens += tokens;\n } else {\n conversationTokens += tokens;\n }\n }\n\n const total = systemTokens + conversationTokens + toolResultTokens;\n\n return {\n estimatedInputTokens: total,\n modelContextLimit: modelLimit,\n utilizationPercent: modelLimit > 0 ? total / modelLimit : 0,\n messageCount: messages.length,\n systemPromptTokens: systemTokens,\n conversationTokens,\n toolResultTokens,\n };\n}\n\n/**\n * Get model context limit with prefix matching fallback.\n */\nexport function getModelContextLimit(model: string): number {\n if (MODEL_CONTEXT_LIMITS[model] !== undefined) {\n return MODEL_CONTEXT_LIMITS[model]!;\n }\n // Prefix match for versioned model names\n for (const [key, limit] of Object.entries(MODEL_CONTEXT_LIMITS)) {\n if (model.startsWith(key)) return limit;\n }\n return 128_000; // Default fallback\n}\n","import type { ModelPricing, TokenUsage } from './types';\n\n/**\n * Calculate cost in microdollars using integer arithmetic only.\n *\n * Pricing values are stored as microdollars per million tokens.\n * Formula: tokens * pricePerMToken / 1_000_000\n * (result is already in microdollars since price is in microdollars)\n *\n * To avoid floating-point, we compute:\n * cost = Math.round(tokens * pricePerMToken / 1_000_000)\n *\n * Since pricePerMToken is already an integer (microdollars per 1M tokens),\n * and tokens is an integer, the only division is by 1_000_000.\n * We use Math.round to get the nearest integer microdollar.\n */\nexport function calculateCostMicrodollars(\n usage: TokenUsage,\n pricing: ModelPricing,\n): number {\n let totalMicrodollars = 0;\n\n // Input tokens cost\n const effectiveInputTokens = usage.inputTokens - (usage.cachedTokens ?? 0);\n if (effectiveInputTokens > 0) {\n totalMicrodollars += integerDivRound(\n effectiveInputTokens * pricing.inputPricePerMToken,\n 1_000_000,\n );\n }\n\n // Cached tokens cost (discounted rate)\n if (usage.cachedTokens && usage.cachedTokens > 0) {\n const cachedPrice = pricing.cachedInputPricePerMToken ?? pricing.inputPricePerMToken;\n totalMicrodollars += integerDivRound(\n usage.cachedTokens * cachedPrice,\n 1_000_000,\n );\n }\n\n // Output tokens cost\n if (usage.outputTokens > 0) {\n totalMicrodollars += integerDivRound(\n usage.outputTokens * pricing.outputPricePerMToken,\n 1_000_000,\n );\n }\n\n // Reasoning tokens cost\n if (usage.reasoningTokens && usage.reasoningTokens > 0) {\n const reasoningPrice = pricing.reasoningPricePerMToken ?? pricing.outputPricePerMToken;\n totalMicrodollars += integerDivRound(\n usage.reasoningTokens * reasoningPrice,\n 1_000_000,\n );\n }\n\n return totalMicrodollars;\n}\n\n/**\n * Integer division with rounding (avoids floating-point).\n * Computes Math.round(numerator / denominator) using only integer ops.\n */\nfunction integerDivRound(numerator: number, denominator: number): number {\n const quotient = Math.trunc(numerator / denominator);\n const remainder = numerator - quotient * denominator;\n // Round: if remainder >= half the denominator, round up\n if (remainder * 2 >= denominator) {\n return quotient + 1;\n }\n return quotient;\n}\n","import type { ModelPricing } from './types';\n\n/**\n * Built-in pricing data — microdollars per million tokens.\n * e.g. $2.50 per 1M tokens = 2_500_000 microdollars per 1M tokens\n */\nconst PRICING: Record<string, Record<string, ModelPricing>> = {\n openai: {\n 'gpt-4o': {\n inputPricePerMToken: 2_500_000,\n outputPricePerMToken: 10_000_000,\n cachedInputPricePerMToken: 1_250_000,\n },\n 'gpt-4o-mini': {\n inputPricePerMToken: 150_000,\n outputPricePerMToken: 600_000,\n cachedInputPricePerMToken: 75_000,\n },\n 'gpt-4.1': {\n inputPricePerMToken: 2_000_000,\n outputPricePerMToken: 8_000_000,\n cachedInputPricePerMToken: 500_000,\n },\n 'gpt-4.1-mini': {\n inputPricePerMToken: 400_000,\n outputPricePerMToken: 1_600_000,\n cachedInputPricePerMToken: 100_000,\n },\n o1: {\n inputPricePerMToken: 15_000_000,\n outputPricePerMToken: 60_000_000,\n reasoningPricePerMToken: 60_000_000,\n cachedInputPricePerMToken: 7_500_000,\n },\n 'o3-mini': {\n inputPricePerMToken: 1_100_000,\n outputPricePerMToken: 4_400_000,\n reasoningPricePerMToken: 4_400_000,\n cachedInputPricePerMToken: 550_000,\n },\n },\n anthropic: {\n 'claude-sonnet-4-20250514': {\n inputPricePerMToken: 3_000_000,\n outputPricePerMToken: 15_000_000,\n cachedInputPricePerMToken: 300_000,\n },\n 'claude-haiku-4-5-20251001': {\n inputPricePerMToken: 800_000,\n outputPricePerMToken: 4_000_000,\n cachedInputPricePerMToken: 80_000,\n },\n 'claude-opus-4-20250514': {\n inputPricePerMToken: 15_000_000,\n outputPricePerMToken: 75_000_000,\n cachedInputPricePerMToken: 1_500_000,\n },\n },\n};\n\nexport function getModelPricing(\n provider: string,\n model: string,\n): ModelPricing | undefined {\n return PRICING[provider]?.[model];\n}\n","import type { ContextAnalysis, Message } from './context';\nimport { analyzeContext } from './context';\nimport { calculateCostMicrodollars } from './cost';\nimport { getModelPricing } from './pricing';\n\nexport type GuardMode = 'notify' | 'block' | 'auto-optimize';\nexport type GuardDecision = 'allow' | 'notify' | 'block' | 'optimized';\n\nexport interface GuardsConfig {\n maxInputTokens?: number;\n maxInputTokensHard?: number;\n maxContextUtilization?: number;\n maxContextUtilizationHard?: number;\n maxCostPerCall?: number;\n maxCostPerCallHard?: number;\n maxCostPerHour?: number;\n mode?: GuardMode;\n notifySlackWebhook?: string;\n notifyDashboard?: boolean;\n onOptimize?: (event: OptimizeEvent) => Promise<OptimizeResult>;\n}\n\nexport interface TriggeredRule {\n ruleType: 'input_tokens' | 'cost_per_call' | 'cost_per_hour' | 'context_utilization' | 'budget';\n currentValue: number;\n threshold: number;\n isHard: boolean;\n}\n\nexport interface GuardCheckResult {\n decision: GuardDecision;\n triggeredRules: TriggeredRule[];\n contextAnalysis: ContextAnalysis | null;\n suggestion?: string;\n}\n\nexport interface GuardEvent {\n eventId: string;\n timestamp: string;\n agentName: string;\n guardMode: GuardMode;\n decision: GuardDecision;\n triggeredRules: TriggeredRule[];\n contextAnalysis?: ContextAnalysis;\n optimization?: {\n action: 'retry' | 'notify' | 'block';\n tokensBefore: number;\n tokensAfter?: number;\n costBefore: number;\n costAfter?: number;\n description?: string;\n };\n suggestion?: string;\n}\n\nexport interface OptimizeEvent {\n type: 'context_utilization' | 'cost_per_call' | 'input_tokens';\n suggestion: string;\n metrics: {\n messages?: Message[];\n model?: string;\n currentValue: number;\n threshold: number;\n };\n}\n\nexport interface OptimizeResult {\n action: 'retry' | 'notify' | 'block';\n messages?: Message[];\n model?: string;\n}\n\n/**\n * Error thrown when guard mode is 'block' and a hard limit is exceeded.\n */\nexport class NeuraMeterGuardError extends Error {\n readonly rule: string;\n readonly current: number;\n readonly threshold: number;\n readonly suggestion: string;\n\n constructor(rule: TriggeredRule, suggestion: string) {\n super(`NeuraMeter guard: ${rule.ruleType} exceeded (${rule.currentValue} > ${rule.threshold})`);\n this.name = 'NeuraMeterGuardError';\n this.rule = rule.ruleType;\n this.current = rule.currentValue;\n this.threshold = rule.threshold;\n this.suggestion = suggestion;\n }\n}\n\n/**\n * Check guard rules before an API call.\n * Returns the decision and any triggered rules.\n */\nexport function checkGuards(\n config: GuardsConfig,\n params: {\n messages: Message[];\n model: string;\n provider: string;\n agentName: string;\n },\n hourlyCostDollars?: number,\n): GuardCheckResult {\n const mode = config.mode ?? 'notify';\n const triggeredRules: TriggeredRule[] = [];\n\n // Context analysis\n const contextAnalysis = analyzeContext(params.messages, params.model);\n\n // Check input tokens\n if (config.maxInputTokens && contextAnalysis.estimatedInputTokens > config.maxInputTokens) {\n triggeredRules.push({\n ruleType: 'input_tokens',\n currentValue: contextAnalysis.estimatedInputTokens,\n threshold: config.maxInputTokens,\n isHard: false,\n });\n }\n if (config.maxInputTokensHard && contextAnalysis.estimatedInputTokens > config.maxInputTokensHard) {\n triggeredRules.push({\n ruleType: 'input_tokens',\n currentValue: contextAnalysis.estimatedInputTokens,\n threshold: config.maxInputTokensHard,\n isHard: true,\n });\n }\n\n // Check context utilization\n if (config.maxContextUtilization && contextAnalysis.utilizationPercent > config.maxContextUtilization) {\n triggeredRules.push({\n ruleType: 'context_utilization',\n currentValue: contextAnalysis.utilizationPercent,\n threshold: config.maxContextUtilization,\n isHard: false,\n });\n }\n if (config.maxContextUtilizationHard && contextAnalysis.utilizationPercent > config.maxContextUtilizationHard) {\n triggeredRules.push({\n ruleType: 'context_utilization',\n currentValue: contextAnalysis.utilizationPercent,\n threshold: config.maxContextUtilizationHard,\n isHard: true,\n });\n }\n\n // Check estimated cost per call\n if (config.maxCostPerCall || config.maxCostPerCallHard) {\n const pricing = getModelPricing(params.provider, params.model);\n if (pricing) {\n const estimatedCost = calculateCostMicrodollars(\n { inputTokens: contextAnalysis.estimatedInputTokens, outputTokens: 0 },\n pricing,\n );\n const estimatedDollars = estimatedCost / 1_000_000;\n\n if (config.maxCostPerCall && estimatedDollars > config.maxCostPerCall) {\n triggeredRules.push({\n ruleType: 'cost_per_call',\n currentValue: estimatedDollars,\n threshold: config.maxCostPerCall,\n isHard: false,\n });\n }\n if (config.maxCostPerCallHard && estimatedDollars > config.maxCostPerCallHard) {\n triggeredRules.push({\n ruleType: 'cost_per_call',\n currentValue: estimatedDollars,\n threshold: config.maxCostPerCallHard,\n isHard: true,\n });\n }\n }\n }\n\n // Check cost per hour\n if (config.maxCostPerHour && hourlyCostDollars !== undefined && hourlyCostDollars > config.maxCostPerHour) {\n triggeredRules.push({\n ruleType: 'cost_per_hour',\n currentValue: hourlyCostDollars,\n threshold: config.maxCostPerHour,\n isHard: mode === 'block',\n });\n }\n\n // Generate suggestion\n const suggestion = generateSuggestion(triggeredRules, contextAnalysis);\n\n // Determine decision based on mode\n if (triggeredRules.length === 0) {\n return { decision: 'allow', triggeredRules, contextAnalysis, suggestion };\n }\n\n const hasHardViolation = triggeredRules.some((r) => r.isHard);\n\n switch (mode) {\n case 'notify':\n return { decision: 'notify', triggeredRules, contextAnalysis, suggestion };\n\n case 'block':\n if (hasHardViolation) {\n return { decision: 'block', triggeredRules, contextAnalysis, suggestion };\n }\n return { decision: 'notify', triggeredRules, contextAnalysis, suggestion };\n\n case 'auto-optimize':\n return { decision: 'notify', triggeredRules, contextAnalysis, suggestion };\n\n default:\n return { decision: 'allow', triggeredRules, contextAnalysis, suggestion };\n }\n}\n\nfunction generateSuggestion(rules: TriggeredRule[], ctx: ContextAnalysis): string {\n if (rules.length === 0) return '';\n\n const parts: string[] = [];\n\n for (const rule of rules) {\n switch (rule.ruleType) {\n case 'context_utilization':\n if (ctx.conversationTokens > ctx.systemPromptTokens) {\n const savingPct = Math.round((ctx.conversationTokens / ctx.estimatedInputTokens) * 100);\n parts.push(`Summarize conversation history to save ~${savingPct}% of input tokens`);\n }\n break;\n case 'input_tokens':\n parts.push(`Reduce input tokens from ${ctx.estimatedInputTokens.toLocaleString()} to under ${rule.threshold.toLocaleString()}`);\n break;\n case 'cost_per_call':\n parts.push('Consider using a cheaper model (e.g., gpt-4o-mini or claude-haiku)');\n break;\n case 'cost_per_hour':\n parts.push(`Hourly cost limit exceeded ($${rule.currentValue.toFixed(2)} > $${rule.threshold.toFixed(2)}). Throttle or pause agent calls`);\n break;\n }\n }\n\n return parts.join('. ') || 'Review guard thresholds or optimize context usage';\n}\n","import type { CostEvent, Provider, TraceOptions, TokenUsage } from './types';\nimport { calculateCostMicrodollars } from './cost';\nimport { getModelPricing } from './pricing';\n\nexport class Trace {\n readonly traceId: string;\n private readonly agentName: string;\n private readonly customerId?: string;\n private readonly taskName?: string;\n private readonly tags?: Record<string, string>;\n private readonly recordFn: (event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId' | 'cost'>) => void;\n\n constructor(\n opts: TraceOptions,\n recordFn: (event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId' | 'cost'>) => void,\n ) {\n this.traceId = crypto.randomUUID();\n this.agentName = opts.agentName;\n this.customerId = opts.customerId;\n this.taskName = opts.taskName;\n this.tags = opts.tags;\n this.recordFn = recordFn;\n }\n\n span(opts: {\n provider: Provider;\n model: string;\n usage: TokenUsage;\n latencyMs: number;\n parentSpanId?: string;\n agentName?: string;\n taskName?: string;\n tags?: Record<string, string>;\n }): string {\n const spanId = crypto.randomUUID();\n const pricing = getModelPricing(opts.provider, opts.model);\n const costMicrodollars = pricing\n ? calculateCostMicrodollars(opts.usage, pricing)\n : 0;\n\n this.recordFn({\n traceId: this.traceId,\n spanId,\n parentSpanId: opts.parentSpanId,\n agentName: opts.agentName ?? this.agentName,\n taskName: opts.taskName ?? this.taskName,\n customerId: this.customerId,\n provider: opts.provider,\n model: opts.model,\n inputTokens: opts.usage.inputTokens,\n outputTokens: opts.usage.outputTokens,\n reasoningTokens: opts.usage.reasoningTokens,\n cachedTokens: opts.usage.cachedTokens,\n costMicrodollars,\n latencyMs: opts.latencyMs,\n tags: { ...this.tags, ...opts.tags },\n });\n\n return spanId;\n }\n}\n","/**\n * Slack notification utility for NeuraMeter guard alerts.\n * Sends Block Kit formatted messages via incoming webhooks.\n */\n\nexport interface SlackMessage {\n /** Plain text fallback */\n text: string;\n /** Name of the agent that triggered the guard */\n agentName: string;\n /** Type of guard rule triggered (e.g. 'context_utilization', 'input_tokens') */\n ruleType: string;\n /** Current value that exceeded the threshold */\n currentValue: number;\n /** Configured threshold that was exceeded */\n threshold: number;\n /** Optimization suggestion from the guard system */\n suggestion?: string;\n}\n\n/**\n * Format a value for display based on the rule type.\n */\nfunction formatValue(ruleType: string, value: number): string {\n switch (ruleType) {\n case 'context_utilization':\n return `${value.toFixed(1)}%`;\n case 'cost_per_call':\n case 'cost_per_hour':\n case 'budget':\n return `$${value.toFixed(4)}`;\n case 'input_tokens':\n return value.toLocaleString();\n default:\n return String(value);\n }\n}\n\n/**\n * Format a rule type string for human-readable display.\n */\nfunction formatRuleType(ruleType: string): string {\n return ruleType\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\n/**\n * Build a Slack Block Kit payload for a guard alert.\n */\nfunction buildSlackPayload(message: SlackMessage): object {\n const blocks: object[] = [\n {\n type: 'header',\n text: {\n type: 'plain_text',\n text: `:warning: NeuraMeter Guard Alert`,\n emoji: true,\n },\n },\n {\n type: 'section',\n fields: [\n {\n type: 'mrkdwn',\n text: `*Agent:*\\n${message.agentName}`,\n },\n {\n type: 'mrkdwn',\n text: `*Rule Triggered:*\\n${formatRuleType(message.ruleType)}`,\n },\n {\n type: 'mrkdwn',\n text: `*Current Value:*\\n${formatValue(message.ruleType, message.currentValue)}`,\n },\n {\n type: 'mrkdwn',\n text: `*Threshold:*\\n${formatValue(message.ruleType, message.threshold)}`,\n },\n ],\n },\n ];\n\n if (message.suggestion) {\n blocks.push({\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `*Suggestion:*\\n${message.suggestion}`,\n },\n });\n }\n\n blocks.push({\n type: 'context',\n elements: [\n {\n type: 'mrkdwn',\n text: `Sent by NeuraMeter at ${new Date().toISOString()}`,\n },\n ],\n });\n\n return {\n text: message.text,\n blocks,\n };\n}\n\n/**\n * Send a Slack notification via an incoming webhook.\n * Fire-and-forget: errors are silently caught and do not propagate.\n *\n * @param webhookUrl - Slack incoming webhook URL\n * @param message - Structured alert message\n */\nexport async function sendSlackNotification(\n webhookUrl: string,\n message: SlackMessage,\n): Promise<void> {\n try {\n const payload = buildSlackPayload(message);\n await fetch(webhookUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n } catch {\n // Fire-and-forget: silently ignore errors\n }\n}\n","import type { CostEvent, NeuraMeterConfig, TraceOptions } from './types';\nimport type { GuardsConfig, GuardCheckResult, GuardEvent, OptimizeEvent, OptimizeResult } from './guards';\nimport { checkGuards, NeuraMeterGuardError } from './guards';\nimport type { Message } from './context';\nimport { Trace } from './trace';\nimport { calculateCostMicrodollars } from './cost';\nimport { getModelPricing } from './pricing';\nimport { sendSlackNotification } from './slack';\n\nconst DEFAULT_BATCH_SIZE = 50;\nconst DEFAULT_FLUSH_INTERVAL_MS = 5_000;\n\n/** Tracks cost accumulated within a rolling 1-hour window per agent. */\ninterface HourlyCostEntry {\n costMicrodollars: number;\n timestamp: number;\n}\n\nexport class NeuraMeter {\n private readonly apiKey: string;\n private readonly projectId: string;\n private readonly endpoint: string;\n private readonly batchSize: number;\n private readonly flushIntervalMs: number;\n readonly guards: GuardsConfig | undefined;\n\n private buffer: CostEvent[] = [];\n private guardBuffer: GuardEvent[] = [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private timer: any = null;\n private flushing = false;\n\n /** Rolling hourly cost tracking per agent for maxCostPerHour guard */\n private hourlyCosts: Map<string, HourlyCostEntry[]> = new Map();\n\n /** Extracted from API key format: nm_{orgId}_{secret} */\n private readonly orgId: string;\n\n constructor(config: NeuraMeterConfig) {\n this.apiKey = config.apiKey;\n this.projectId = config.projectId;\n this.endpoint = config.endpoint ?? 'https://neurameter-ingestion.neurameter.workers.dev';\n this.batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\n this.flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n this.guards = config.guards;\n\n // Extract orgId from API key (format: nm_{orgId}_{secret})\n const parts = this.apiKey.split('_');\n this.orgId = parts.length >= 3 ? parts[1]! : 'unknown';\n\n this.startAutoFlush();\n }\n\n startTrace(opts: TraceOptions): Trace {\n return new Trace(opts, (event) => this.record(event));\n }\n\n /**\n * Get rolling hourly cost for an agent (in dollars).\n */\n getHourlyCostDollars(agentName: string): number {\n const oneHourAgo = Date.now() - 3_600_000;\n const entries = this.hourlyCosts.get(agentName) ?? [];\n // Prune old entries\n const recent = entries.filter((e) => e.timestamp > oneHourAgo);\n this.hourlyCosts.set(agentName, recent);\n const totalMicro = recent.reduce((s, e) => s + e.costMicrodollars, 0);\n return totalMicro / 1_000_000;\n }\n\n /**\n * Track cost for hourly rate limiting.\n */\n private trackHourlyCost(agentName: string, costMicrodollars: number): void {\n const entries = this.hourlyCosts.get(agentName) ?? [];\n entries.push({ costMicrodollars, timestamp: Date.now() });\n this.hourlyCosts.set(agentName, entries);\n }\n\n /**\n * Check guard rules before an API call.\n * Returns the check result. In block mode, may throw NeuraMeterGuardError.\n */\n checkGuards(params: {\n messages: Message[];\n model: string;\n provider: string;\n agentName: string;\n }): GuardCheckResult | null {\n if (!this.guards) return null;\n\n const hourlyCostDollars = this.getHourlyCostDollars(params.agentName);\n const result = checkGuards(this.guards, params, hourlyCostDollars);\n\n // Record guard event (async, fire-and-forget)\n if (result.triggeredRules.length > 0) {\n this.recordGuardEvent({\n eventId: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n agentName: params.agentName,\n guardMode: this.guards.mode ?? 'notify',\n decision: result.decision,\n triggeredRules: result.triggeredRules,\n contextAnalysis: result.contextAnalysis ?? undefined,\n suggestion: result.suggestion,\n });\n\n // Send Slack notification if configured (async, fire-and-forget)\n if (this.guards.notifySlackWebhook) {\n for (const rule of result.triggeredRules) {\n void sendSlackNotification(this.guards.notifySlackWebhook, {\n text: `NeuraMeter guard triggered: ${rule.ruleType} for agent \"${params.agentName}\" (${rule.currentValue} > ${rule.threshold})`,\n agentName: params.agentName,\n ruleType: rule.ruleType,\n currentValue: rule.currentValue,\n threshold: rule.threshold,\n suggestion: result.suggestion,\n });\n }\n }\n }\n\n // In block mode with hard violation, throw\n if (result.decision === 'block') {\n const hardRule = result.triggeredRules.find((r) => r.isHard);\n if (hardRule) {\n throw new NeuraMeterGuardError(hardRule, result.suggestion ?? '');\n }\n }\n\n return result;\n }\n\n /**\n * Run auto-optimize flow: calls the onOptimize callback and returns the result.\n * Used by SDK wrappers when mode is 'auto-optimize' and thresholds are exceeded.\n */\n async runAutoOptimize(params: {\n guardResult: GuardCheckResult;\n messages: Message[];\n model: string;\n agentName: string;\n }): Promise<OptimizeResult | null> {\n if (!this.guards?.onOptimize || params.guardResult.triggeredRules.length === 0) {\n return null;\n }\n\n const primaryRule = params.guardResult.triggeredRules[0]!;\n const optimizeEvent: OptimizeEvent = {\n type: primaryRule.ruleType as OptimizeEvent['type'],\n suggestion: params.guardResult.suggestion ?? '',\n metrics: {\n messages: params.messages,\n model: params.model,\n currentValue: primaryRule.currentValue,\n threshold: primaryRule.threshold,\n },\n };\n\n try {\n const result = await this.guards.onOptimize(optimizeEvent);\n\n // Record optimization in guard event\n const pricing = getModelPricing(\n 'openai', // Best effort — wrappers will provide actual provider\n result.model ?? params.model,\n );\n const tokensBefore = params.guardResult.contextAnalysis?.estimatedInputTokens ?? 0;\n const costBefore = pricing\n ? calculateCostMicrodollars({ inputTokens: tokensBefore, outputTokens: 0 }, pricing) / 1_000_000\n : 0;\n\n this.recordGuardEvent({\n eventId: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n agentName: params.agentName,\n guardMode: 'auto-optimize',\n decision: result.action === 'retry' ? 'optimized' : result.action === 'block' ? 'block' : 'notify',\n triggeredRules: params.guardResult.triggeredRules,\n contextAnalysis: params.guardResult.contextAnalysis ?? undefined,\n optimization: {\n action: result.action,\n tokensBefore,\n costBefore,\n description: params.guardResult.suggestion,\n },\n suggestion: params.guardResult.suggestion,\n });\n\n return result;\n } catch {\n // If onOptimize fails, fall through to notify\n return { action: 'notify' };\n }\n }\n\n record(event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId' | 'cost'>): void {\n const fullEvent: CostEvent = {\n ...event,\n eventId: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n orgId: this.orgId,\n projectId: this.projectId,\n cost: event.costMicrodollars / 1_000_000,\n };\n\n this.buffer.push(fullEvent);\n\n // Track hourly cost for rate limiting\n if (event.agentName && event.costMicrodollars > 0) {\n this.trackHourlyCost(event.agentName, event.costMicrodollars);\n }\n\n if (this.buffer.length >= this.batchSize) {\n void this.flush();\n }\n }\n\n private recordGuardEvent(event: GuardEvent): void {\n this.guardBuffer.push(event);\n // Flush guard events alongside regular events\n }\n\n async flush(): Promise<void> {\n if ((this.buffer.length === 0 && this.guardBuffer.length === 0) || this.flushing) return;\n\n this.flushing = true;\n\n // Flush cost events\n if (this.buffer.length > 0) {\n const batch = this.buffer.splice(0, this.batchSize);\n try {\n const response = await fetch(`${this.endpoint}/v1/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ batch }),\n });\n if (!response.ok) {\n this.buffer.unshift(...batch);\n }\n } catch {\n this.buffer.unshift(...batch);\n }\n }\n\n // Flush guard events\n if (this.guardBuffer.length > 0) {\n const guardBatch = this.guardBuffer.splice(0, this.batchSize);\n try {\n await fetch(`${this.endpoint}/v1/guard-events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ batch: guardBatch }),\n });\n } catch {\n // Fire-and-forget for guard events\n }\n }\n\n this.flushing = false;\n }\n\n async destroy(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n\n private startAutoFlush(): void {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushIntervalMs);\n\n if (this.timer && typeof this.timer === 'object' && 'unref' in this.timer) {\n (this.timer as { unref: () => void }).unref();\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/cost.ts","../src/pricing.ts","../src/guards.ts","../src/trace.ts","../src/slack.ts","../src/meter.ts"],"names":["MODEL_CONTEXT_LIMITS","estimateTokens","content","str","analyzeContext","messages","model","modelLimit","getModelContextLimit","systemTokens","conversationTokens","toolResultTokens","msg","tokens","total","key","limit","calculateCostMicrodollars","usage","pricing","totalMicrodollars","effectiveInputTokens","integerDivRound","cachedPrice","reasoningPrice","numerator","denominator","quotient","PRICING","getModelPricing","provider","providerPricing","NeuraMeterGuardError","rule","suggestion","checkGuards","config","params","hourlyCostDollars","mode","triggeredRules","contextAnalysis","estimatedDollars","generateSuggestion","hasHardViolation","r","rules","ctx","parts","savingPct","Trace","opts","recordFn","spanId","costMicrodollars","formatValue","ruleType","value","formatRuleType","word","buildSlackPayload","message","blocks","sendSlackNotification","webhookUrl","payload","DEFAULT_BATCH_SIZE","DEFAULT_FLUSH_INTERVAL_MS","NeuraMeter","event","agentName","oneHourAgo","recent","e","entries","result","hardRule","primaryRule","optimizeEvent","tokensBefore","costBefore","fullEvent","batch","guardBatch"],"mappings":"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","file":"index.cjs","sourcesContent":["export interface ContextAnalysis {\n estimatedInputTokens: number;\n modelContextLimit: number;\n utilizationPercent: number;\n messageCount: number;\n systemPromptTokens: number;\n conversationTokens: number;\n toolResultTokens: number;\n}\n\nexport interface Message {\n role: string;\n content: string | unknown;\n}\n\n/**\n * Model context window limits (tokens).\n */\nexport const MODEL_CONTEXT_LIMITS: Record<string, number> = {\n 'gpt-4o': 128_000,\n 'gpt-4o-mini': 128_000,\n 'gpt-4.1': 1_000_000,\n 'gpt-4.1-mini': 1_000_000,\n 'o1': 200_000,\n 'o3-mini': 200_000,\n 'claude-sonnet-4-20250514': 200_000,\n 'claude-haiku-4-5-20251001': 200_000,\n 'claude-opus-4-20250514': 200_000,\n // Aliases for prefix matching\n 'claude-sonnet-4': 200_000,\n 'claude-haiku-4': 200_000,\n 'claude-opus-4': 200_000,\n};\n\n/**\n * Estimate token count from a string.\n * Uses a simple heuristic: ~4 chars per token for English,\n * ~1.5 chars per token for CJK/mixed content.\n * This is intentionally fast (<1ms) for SDK use.\n */\nexport function estimateTokens(content: string | unknown): number {\n if (typeof content !== 'string') {\n // For non-string content (e.g., tool_use blocks), serialize and estimate\n const str = typeof content === 'object' ? JSON.stringify(content) : String(content ?? '');\n return Math.ceil(str.length / 4);\n }\n // Simple heuristic: average of ~4 chars/token\n return Math.ceil(content.length / 4);\n}\n\n/**\n * Analyze context window utilization for a set of messages.\n */\nexport function analyzeContext(messages: Message[], model: string): ContextAnalysis {\n const modelLimit = getModelContextLimit(model);\n let systemTokens = 0;\n let conversationTokens = 0;\n let toolResultTokens = 0;\n\n for (const msg of messages) {\n const tokens = estimateTokens(msg.content);\n if (msg.role === 'system') {\n systemTokens += tokens;\n } else if (msg.role === 'tool') {\n toolResultTokens += tokens;\n } else {\n conversationTokens += tokens;\n }\n }\n\n const total = systemTokens + conversationTokens + toolResultTokens;\n\n return {\n estimatedInputTokens: total,\n modelContextLimit: modelLimit,\n utilizationPercent: modelLimit > 0 ? total / modelLimit : 0,\n messageCount: messages.length,\n systemPromptTokens: systemTokens,\n conversationTokens,\n toolResultTokens,\n };\n}\n\n/**\n * Get model context limit with prefix matching fallback.\n */\nexport function getModelContextLimit(model: string): number {\n if (MODEL_CONTEXT_LIMITS[model] !== undefined) {\n return MODEL_CONTEXT_LIMITS[model]!;\n }\n // Prefix match for versioned model names\n for (const [key, limit] of Object.entries(MODEL_CONTEXT_LIMITS)) {\n if (model.startsWith(key)) return limit;\n }\n return 128_000; // Default fallback\n}\n","import type { ModelPricing, TokenUsage } from './types';\n\n/**\n * Calculate cost in microdollars using integer arithmetic only.\n *\n * Pricing values are stored as microdollars per million tokens.\n * Formula: tokens * pricePerMToken / 1_000_000\n * (result is already in microdollars since price is in microdollars)\n *\n * To avoid floating-point, we compute:\n * cost = Math.round(tokens * pricePerMToken / 1_000_000)\n *\n * Since pricePerMToken is already an integer (microdollars per 1M tokens),\n * and tokens is an integer, the only division is by 1_000_000.\n * We use Math.round to get the nearest integer microdollar.\n */\nexport function calculateCostMicrodollars(\n usage: TokenUsage,\n pricing: ModelPricing,\n): number {\n let totalMicrodollars = 0;\n\n // Input tokens cost\n const effectiveInputTokens = usage.inputTokens - (usage.cachedTokens ?? 0);\n if (effectiveInputTokens > 0) {\n totalMicrodollars += integerDivRound(\n effectiveInputTokens * pricing.inputPricePerMToken,\n 1_000_000,\n );\n }\n\n // Cached tokens cost (discounted rate)\n if (usage.cachedTokens && usage.cachedTokens > 0) {\n const cachedPrice = pricing.cachedInputPricePerMToken ?? pricing.inputPricePerMToken;\n totalMicrodollars += integerDivRound(\n usage.cachedTokens * cachedPrice,\n 1_000_000,\n );\n }\n\n // Output tokens cost\n if (usage.outputTokens > 0) {\n totalMicrodollars += integerDivRound(\n usage.outputTokens * pricing.outputPricePerMToken,\n 1_000_000,\n );\n }\n\n // Reasoning tokens cost\n if (usage.reasoningTokens && usage.reasoningTokens > 0) {\n const reasoningPrice = pricing.reasoningPricePerMToken ?? pricing.outputPricePerMToken;\n totalMicrodollars += integerDivRound(\n usage.reasoningTokens * reasoningPrice,\n 1_000_000,\n );\n }\n\n return totalMicrodollars;\n}\n\n/**\n * Integer division with rounding (avoids floating-point).\n * Computes Math.round(numerator / denominator) using only integer ops.\n */\nfunction integerDivRound(numerator: number, denominator: number): number {\n const quotient = Math.trunc(numerator / denominator);\n const remainder = numerator - quotient * denominator;\n // Round: if remainder >= half the denominator, round up\n if (remainder * 2 >= denominator) {\n return quotient + 1;\n }\n return quotient;\n}\n","import type { ModelPricing } from './types';\n\n/**\n * Built-in pricing data — microdollars per million tokens.\n * e.g. $2.50 per 1M tokens = 2_500_000 microdollars per 1M tokens\n */\nconst PRICING: Record<string, Record<string, ModelPricing>> = {\n openai: {\n 'gpt-4o': {\n inputPricePerMToken: 2_500_000,\n outputPricePerMToken: 10_000_000,\n cachedInputPricePerMToken: 1_250_000,\n },\n 'gpt-4o-mini': {\n inputPricePerMToken: 150_000,\n outputPricePerMToken: 600_000,\n cachedInputPricePerMToken: 75_000,\n },\n 'gpt-4.1': {\n inputPricePerMToken: 2_000_000,\n outputPricePerMToken: 8_000_000,\n cachedInputPricePerMToken: 500_000,\n },\n 'gpt-4.1-mini': {\n inputPricePerMToken: 400_000,\n outputPricePerMToken: 1_600_000,\n cachedInputPricePerMToken: 100_000,\n },\n o1: {\n inputPricePerMToken: 15_000_000,\n outputPricePerMToken: 60_000_000,\n reasoningPricePerMToken: 60_000_000,\n cachedInputPricePerMToken: 7_500_000,\n },\n 'o3-mini': {\n inputPricePerMToken: 1_100_000,\n outputPricePerMToken: 4_400_000,\n reasoningPricePerMToken: 4_400_000,\n cachedInputPricePerMToken: 550_000,\n },\n },\n anthropic: {\n 'claude-sonnet-4-20250514': {\n inputPricePerMToken: 3_000_000,\n outputPricePerMToken: 15_000_000,\n cachedInputPricePerMToken: 300_000,\n },\n 'claude-haiku-4-5-20251001': {\n inputPricePerMToken: 800_000,\n outputPricePerMToken: 4_000_000,\n cachedInputPricePerMToken: 80_000,\n },\n 'claude-opus-4-20250514': {\n inputPricePerMToken: 15_000_000,\n outputPricePerMToken: 75_000_000,\n cachedInputPricePerMToken: 1_500_000,\n },\n },\n};\n\nexport function getModelPricing(\n provider: string,\n model: string,\n): ModelPricing | undefined {\n const providerPricing = PRICING[provider];\n if (!providerPricing) return undefined;\n\n // Exact match first\n if (providerPricing[model]) return providerPricing[model];\n\n // Prefix match: e.g. \"gpt-4o-mini-2024-07-18\" → \"gpt-4o-mini\"\n for (const key of Object.keys(providerPricing)) {\n if (model.startsWith(key)) return providerPricing[key];\n }\n\n return undefined;\n}\n","import type { ContextAnalysis, Message } from './context';\nimport { analyzeContext } from './context';\nimport { calculateCostMicrodollars } from './cost';\nimport { getModelPricing } from './pricing';\n\nexport type GuardMode = 'notify' | 'block' | 'auto-optimize';\nexport type GuardDecision = 'allow' | 'notify' | 'block' | 'optimized';\n\nexport interface GuardsConfig {\n maxInputTokens?: number;\n maxInputTokensHard?: number;\n maxContextUtilization?: number;\n maxContextUtilizationHard?: number;\n maxCostPerCall?: number;\n maxCostPerCallHard?: number;\n maxCostPerHour?: number;\n mode?: GuardMode;\n notifySlackWebhook?: string;\n notifyDashboard?: boolean;\n onOptimize?: (event: OptimizeEvent) => Promise<OptimizeResult>;\n}\n\nexport interface TriggeredRule {\n ruleType: 'input_tokens' | 'cost_per_call' | 'cost_per_hour' | 'context_utilization' | 'budget';\n currentValue: number;\n threshold: number;\n isHard: boolean;\n}\n\nexport interface GuardCheckResult {\n decision: GuardDecision;\n triggeredRules: TriggeredRule[];\n contextAnalysis: ContextAnalysis | null;\n suggestion?: string;\n}\n\nexport interface GuardEvent {\n eventId: string;\n timestamp: string;\n agentName: string;\n guardMode: GuardMode;\n decision: GuardDecision;\n triggeredRules: TriggeredRule[];\n contextAnalysis?: ContextAnalysis;\n optimization?: {\n action: 'retry' | 'notify' | 'block';\n tokensBefore: number;\n tokensAfter?: number;\n costBefore: number;\n costAfter?: number;\n description?: string;\n };\n suggestion?: string;\n}\n\nexport interface OptimizeEvent {\n type: 'context_utilization' | 'cost_per_call' | 'input_tokens';\n suggestion: string;\n metrics: {\n messages?: Message[];\n model?: string;\n currentValue: number;\n threshold: number;\n };\n}\n\nexport interface OptimizeResult {\n action: 'retry' | 'notify' | 'block';\n messages?: Message[];\n model?: string;\n}\n\n/**\n * Error thrown when guard mode is 'block' and a hard limit is exceeded.\n */\nexport class NeuraMeterGuardError extends Error {\n readonly rule: string;\n readonly current: number;\n readonly threshold: number;\n readonly suggestion: string;\n\n constructor(rule: TriggeredRule, suggestion: string) {\n super(`NeuraMeter guard: ${rule.ruleType} exceeded (${rule.currentValue} > ${rule.threshold})`);\n this.name = 'NeuraMeterGuardError';\n this.rule = rule.ruleType;\n this.current = rule.currentValue;\n this.threshold = rule.threshold;\n this.suggestion = suggestion;\n }\n}\n\n/**\n * Check guard rules before an API call.\n * Returns the decision and any triggered rules.\n */\nexport function checkGuards(\n config: GuardsConfig,\n params: {\n messages: Message[];\n model: string;\n provider: string;\n agentName: string;\n },\n hourlyCostDollars?: number,\n): GuardCheckResult {\n const mode = config.mode ?? 'notify';\n const triggeredRules: TriggeredRule[] = [];\n\n // Context analysis\n const contextAnalysis = analyzeContext(params.messages, params.model);\n\n // Check input tokens\n if (config.maxInputTokens && contextAnalysis.estimatedInputTokens > config.maxInputTokens) {\n triggeredRules.push({\n ruleType: 'input_tokens',\n currentValue: contextAnalysis.estimatedInputTokens,\n threshold: config.maxInputTokens,\n isHard: false,\n });\n }\n if (config.maxInputTokensHard && contextAnalysis.estimatedInputTokens > config.maxInputTokensHard) {\n triggeredRules.push({\n ruleType: 'input_tokens',\n currentValue: contextAnalysis.estimatedInputTokens,\n threshold: config.maxInputTokensHard,\n isHard: true,\n });\n }\n\n // Check context utilization\n if (config.maxContextUtilization && contextAnalysis.utilizationPercent > config.maxContextUtilization) {\n triggeredRules.push({\n ruleType: 'context_utilization',\n currentValue: contextAnalysis.utilizationPercent,\n threshold: config.maxContextUtilization,\n isHard: false,\n });\n }\n if (config.maxContextUtilizationHard && contextAnalysis.utilizationPercent > config.maxContextUtilizationHard) {\n triggeredRules.push({\n ruleType: 'context_utilization',\n currentValue: contextAnalysis.utilizationPercent,\n threshold: config.maxContextUtilizationHard,\n isHard: true,\n });\n }\n\n // Check estimated cost per call\n if (config.maxCostPerCall || config.maxCostPerCallHard) {\n const pricing = getModelPricing(params.provider, params.model);\n if (pricing) {\n const estimatedCost = calculateCostMicrodollars(\n { inputTokens: contextAnalysis.estimatedInputTokens, outputTokens: 0 },\n pricing,\n );\n const estimatedDollars = estimatedCost / 1_000_000;\n\n if (config.maxCostPerCall && estimatedDollars > config.maxCostPerCall) {\n triggeredRules.push({\n ruleType: 'cost_per_call',\n currentValue: estimatedDollars,\n threshold: config.maxCostPerCall,\n isHard: false,\n });\n }\n if (config.maxCostPerCallHard && estimatedDollars > config.maxCostPerCallHard) {\n triggeredRules.push({\n ruleType: 'cost_per_call',\n currentValue: estimatedDollars,\n threshold: config.maxCostPerCallHard,\n isHard: true,\n });\n }\n }\n }\n\n // Check cost per hour\n if (config.maxCostPerHour && hourlyCostDollars !== undefined && hourlyCostDollars > config.maxCostPerHour) {\n triggeredRules.push({\n ruleType: 'cost_per_hour',\n currentValue: hourlyCostDollars,\n threshold: config.maxCostPerHour,\n isHard: mode === 'block',\n });\n }\n\n // Generate suggestion\n const suggestion = generateSuggestion(triggeredRules, contextAnalysis);\n\n // Determine decision based on mode\n if (triggeredRules.length === 0) {\n return { decision: 'allow', triggeredRules, contextAnalysis, suggestion };\n }\n\n const hasHardViolation = triggeredRules.some((r) => r.isHard);\n\n switch (mode) {\n case 'notify':\n return { decision: 'notify', triggeredRules, contextAnalysis, suggestion };\n\n case 'block':\n if (hasHardViolation) {\n return { decision: 'block', triggeredRules, contextAnalysis, suggestion };\n }\n return { decision: 'notify', triggeredRules, contextAnalysis, suggestion };\n\n case 'auto-optimize':\n return { decision: 'notify', triggeredRules, contextAnalysis, suggestion };\n\n default:\n return { decision: 'allow', triggeredRules, contextAnalysis, suggestion };\n }\n}\n\nfunction generateSuggestion(rules: TriggeredRule[], ctx: ContextAnalysis): string {\n if (rules.length === 0) return '';\n\n const parts: string[] = [];\n\n for (const rule of rules) {\n switch (rule.ruleType) {\n case 'context_utilization':\n if (ctx.conversationTokens > ctx.systemPromptTokens) {\n const savingPct = Math.round((ctx.conversationTokens / ctx.estimatedInputTokens) * 100);\n parts.push(`Summarize conversation history to save ~${savingPct}% of input tokens`);\n }\n break;\n case 'input_tokens':\n parts.push(`Reduce input tokens from ${ctx.estimatedInputTokens.toLocaleString()} to under ${rule.threshold.toLocaleString()}`);\n break;\n case 'cost_per_call':\n parts.push('Consider using a cheaper model (e.g., gpt-4o-mini or claude-haiku)');\n break;\n case 'cost_per_hour':\n parts.push(`Hourly cost limit exceeded ($${rule.currentValue.toFixed(2)} > $${rule.threshold.toFixed(2)}). Throttle or pause agent calls`);\n break;\n }\n }\n\n return parts.join('. ') || 'Review guard thresholds or optimize context usage';\n}\n","import type { CostEvent, Provider, TraceOptions, TokenUsage } from './types';\nimport { calculateCostMicrodollars } from './cost';\nimport { getModelPricing } from './pricing';\n\nexport class Trace {\n readonly traceId: string;\n private readonly agentName: string;\n private readonly customerId?: string;\n private readonly taskName?: string;\n private readonly tags?: Record<string, string>;\n private readonly recordFn: (event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId' | 'cost'>) => void;\n\n constructor(\n opts: TraceOptions,\n recordFn: (event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId' | 'cost'>) => void,\n ) {\n this.traceId = crypto.randomUUID();\n this.agentName = opts.agentName;\n this.customerId = opts.customerId;\n this.taskName = opts.taskName;\n this.tags = opts.tags;\n this.recordFn = recordFn;\n }\n\n span(opts: {\n provider: Provider;\n model: string;\n usage: TokenUsage;\n latencyMs: number;\n parentSpanId?: string;\n agentName?: string;\n taskName?: string;\n tags?: Record<string, string>;\n }): string {\n const spanId = crypto.randomUUID();\n const pricing = getModelPricing(opts.provider, opts.model);\n const costMicrodollars = pricing\n ? calculateCostMicrodollars(opts.usage, pricing)\n : 0;\n\n this.recordFn({\n traceId: this.traceId,\n spanId,\n parentSpanId: opts.parentSpanId,\n agentName: opts.agentName ?? this.agentName,\n taskName: opts.taskName ?? this.taskName,\n customerId: this.customerId,\n provider: opts.provider,\n model: opts.model,\n inputTokens: opts.usage.inputTokens,\n outputTokens: opts.usage.outputTokens,\n reasoningTokens: opts.usage.reasoningTokens,\n cachedTokens: opts.usage.cachedTokens,\n costMicrodollars,\n latencyMs: opts.latencyMs,\n tags: { ...this.tags, ...opts.tags },\n });\n\n return spanId;\n }\n}\n","/**\n * Slack notification utility for NeuraMeter guard alerts.\n * Sends Block Kit formatted messages via incoming webhooks.\n */\n\nexport interface SlackMessage {\n /** Plain text fallback */\n text: string;\n /** Name of the agent that triggered the guard */\n agentName: string;\n /** Type of guard rule triggered (e.g. 'context_utilization', 'input_tokens') */\n ruleType: string;\n /** Current value that exceeded the threshold */\n currentValue: number;\n /** Configured threshold that was exceeded */\n threshold: number;\n /** Optimization suggestion from the guard system */\n suggestion?: string;\n}\n\n/**\n * Format a value for display based on the rule type.\n */\nfunction formatValue(ruleType: string, value: number): string {\n switch (ruleType) {\n case 'context_utilization':\n return `${value.toFixed(1)}%`;\n case 'cost_per_call':\n case 'cost_per_hour':\n case 'budget':\n return `$${value.toFixed(4)}`;\n case 'input_tokens':\n return value.toLocaleString();\n default:\n return String(value);\n }\n}\n\n/**\n * Format a rule type string for human-readable display.\n */\nfunction formatRuleType(ruleType: string): string {\n return ruleType\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\n/**\n * Build a Slack Block Kit payload for a guard alert.\n */\nfunction buildSlackPayload(message: SlackMessage): object {\n const blocks: object[] = [\n {\n type: 'header',\n text: {\n type: 'plain_text',\n text: `:warning: NeuraMeter Guard Alert`,\n emoji: true,\n },\n },\n {\n type: 'section',\n fields: [\n {\n type: 'mrkdwn',\n text: `*Agent:*\\n${message.agentName}`,\n },\n {\n type: 'mrkdwn',\n text: `*Rule Triggered:*\\n${formatRuleType(message.ruleType)}`,\n },\n {\n type: 'mrkdwn',\n text: `*Current Value:*\\n${formatValue(message.ruleType, message.currentValue)}`,\n },\n {\n type: 'mrkdwn',\n text: `*Threshold:*\\n${formatValue(message.ruleType, message.threshold)}`,\n },\n ],\n },\n ];\n\n if (message.suggestion) {\n blocks.push({\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `*Suggestion:*\\n${message.suggestion}`,\n },\n });\n }\n\n blocks.push({\n type: 'context',\n elements: [\n {\n type: 'mrkdwn',\n text: `Sent by NeuraMeter at ${new Date().toISOString()}`,\n },\n ],\n });\n\n return {\n text: message.text,\n blocks,\n };\n}\n\n/**\n * Send a Slack notification via an incoming webhook.\n * Fire-and-forget: errors are silently caught and do not propagate.\n *\n * @param webhookUrl - Slack incoming webhook URL\n * @param message - Structured alert message\n */\nexport async function sendSlackNotification(\n webhookUrl: string,\n message: SlackMessage,\n): Promise<void> {\n try {\n const payload = buildSlackPayload(message);\n await fetch(webhookUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n } catch {\n // Fire-and-forget: silently ignore errors\n }\n}\n","import type { CostEvent, NeuraMeterConfig, TraceOptions } from './types';\nimport type { GuardsConfig, GuardCheckResult, GuardEvent, OptimizeEvent, OptimizeResult } from './guards';\nimport { checkGuards, NeuraMeterGuardError } from './guards';\nimport type { Message } from './context';\nimport { Trace } from './trace';\nimport { calculateCostMicrodollars } from './cost';\nimport { getModelPricing } from './pricing';\nimport { sendSlackNotification } from './slack';\n\nconst DEFAULT_BATCH_SIZE = 50;\nconst DEFAULT_FLUSH_INTERVAL_MS = 5_000;\n\n/** Tracks cost accumulated within a rolling 1-hour window per agent. */\ninterface HourlyCostEntry {\n costMicrodollars: number;\n timestamp: number;\n}\n\nexport class NeuraMeter {\n private readonly apiKey: string;\n private readonly projectId: string;\n private readonly endpoint: string;\n private readonly batchSize: number;\n private readonly flushIntervalMs: number;\n readonly guards: GuardsConfig | undefined;\n\n private buffer: CostEvent[] = [];\n private guardBuffer: GuardEvent[] = [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private timer: any = null;\n private flushing = false;\n\n /** Rolling hourly cost tracking per agent for maxCostPerHour guard */\n private hourlyCosts: Map<string, HourlyCostEntry[]> = new Map();\n\n /** Extracted from API key format: nm_{orgId}_{secret} */\n private readonly orgId: string;\n\n constructor(config: NeuraMeterConfig) {\n this.apiKey = config.apiKey;\n this.projectId = config.projectId;\n this.endpoint = config.endpoint ?? 'https://neurameter-ingestion.neurameter.workers.dev';\n this.batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\n this.flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n this.guards = config.guards;\n\n // Extract orgId from API key (format: nm_{orgId}_{secret})\n const parts = this.apiKey.split('_');\n this.orgId = parts.length >= 3 ? parts[1]! : 'unknown';\n\n this.startAutoFlush();\n }\n\n startTrace(opts: TraceOptions): Trace {\n return new Trace(opts, (event) => this.record(event));\n }\n\n /**\n * Get rolling hourly cost for an agent (in dollars).\n */\n getHourlyCostDollars(agentName: string): number {\n const oneHourAgo = Date.now() - 3_600_000;\n const entries = this.hourlyCosts.get(agentName) ?? [];\n // Prune old entries\n const recent = entries.filter((e) => e.timestamp > oneHourAgo);\n this.hourlyCosts.set(agentName, recent);\n const totalMicro = recent.reduce((s, e) => s + e.costMicrodollars, 0);\n return totalMicro / 1_000_000;\n }\n\n /**\n * Track cost for hourly rate limiting.\n */\n private trackHourlyCost(agentName: string, costMicrodollars: number): void {\n const entries = this.hourlyCosts.get(agentName) ?? [];\n entries.push({ costMicrodollars, timestamp: Date.now() });\n this.hourlyCosts.set(agentName, entries);\n }\n\n /**\n * Check guard rules before an API call.\n * Returns the check result. In block mode, may throw NeuraMeterGuardError.\n */\n checkGuards(params: {\n messages: Message[];\n model: string;\n provider: string;\n agentName: string;\n }): GuardCheckResult | null {\n if (!this.guards) return null;\n\n const hourlyCostDollars = this.getHourlyCostDollars(params.agentName);\n const result = checkGuards(this.guards, params, hourlyCostDollars);\n\n // Record guard event (async, fire-and-forget)\n if (result.triggeredRules.length > 0) {\n this.recordGuardEvent({\n eventId: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n agentName: params.agentName,\n guardMode: this.guards.mode ?? 'notify',\n decision: result.decision,\n triggeredRules: result.triggeredRules,\n contextAnalysis: result.contextAnalysis ?? undefined,\n suggestion: result.suggestion,\n });\n\n // Send Slack notification if configured (async, fire-and-forget)\n if (this.guards.notifySlackWebhook) {\n for (const rule of result.triggeredRules) {\n void sendSlackNotification(this.guards.notifySlackWebhook, {\n text: `NeuraMeter guard triggered: ${rule.ruleType} for agent \"${params.agentName}\" (${rule.currentValue} > ${rule.threshold})`,\n agentName: params.agentName,\n ruleType: rule.ruleType,\n currentValue: rule.currentValue,\n threshold: rule.threshold,\n suggestion: result.suggestion,\n });\n }\n }\n }\n\n // In block mode with hard violation, throw\n if (result.decision === 'block') {\n const hardRule = result.triggeredRules.find((r) => r.isHard);\n if (hardRule) {\n throw new NeuraMeterGuardError(hardRule, result.suggestion ?? '');\n }\n }\n\n return result;\n }\n\n /**\n * Run auto-optimize flow: calls the onOptimize callback and returns the result.\n * Used by SDK wrappers when mode is 'auto-optimize' and thresholds are exceeded.\n */\n async runAutoOptimize(params: {\n guardResult: GuardCheckResult;\n messages: Message[];\n model: string;\n agentName: string;\n }): Promise<OptimizeResult | null> {\n if (!this.guards?.onOptimize || params.guardResult.triggeredRules.length === 0) {\n return null;\n }\n\n const primaryRule = params.guardResult.triggeredRules[0]!;\n const optimizeEvent: OptimizeEvent = {\n type: primaryRule.ruleType as OptimizeEvent['type'],\n suggestion: params.guardResult.suggestion ?? '',\n metrics: {\n messages: params.messages,\n model: params.model,\n currentValue: primaryRule.currentValue,\n threshold: primaryRule.threshold,\n },\n };\n\n try {\n const result = await this.guards.onOptimize(optimizeEvent);\n\n // Record optimization in guard event\n const pricing = getModelPricing(\n 'openai', // Best effort — wrappers will provide actual provider\n result.model ?? params.model,\n );\n const tokensBefore = params.guardResult.contextAnalysis?.estimatedInputTokens ?? 0;\n const costBefore = pricing\n ? calculateCostMicrodollars({ inputTokens: tokensBefore, outputTokens: 0 }, pricing) / 1_000_000\n : 0;\n\n this.recordGuardEvent({\n eventId: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n agentName: params.agentName,\n guardMode: 'auto-optimize',\n decision: result.action === 'retry' ? 'optimized' : result.action === 'block' ? 'block' : 'notify',\n triggeredRules: params.guardResult.triggeredRules,\n contextAnalysis: params.guardResult.contextAnalysis ?? undefined,\n optimization: {\n action: result.action,\n tokensBefore,\n costBefore,\n description: params.guardResult.suggestion,\n },\n suggestion: params.guardResult.suggestion,\n });\n\n return result;\n } catch {\n // If onOptimize fails, fall through to notify\n return { action: 'notify' };\n }\n }\n\n record(event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId' | 'cost'>): void {\n const fullEvent: CostEvent = {\n ...event,\n eventId: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n orgId: this.orgId,\n projectId: this.projectId,\n cost: event.costMicrodollars / 1_000_000,\n };\n\n this.buffer.push(fullEvent);\n\n // Track hourly cost for rate limiting\n if (event.agentName && event.costMicrodollars > 0) {\n this.trackHourlyCost(event.agentName, event.costMicrodollars);\n }\n\n if (this.buffer.length >= this.batchSize) {\n void this.flush();\n }\n }\n\n private recordGuardEvent(event: GuardEvent): void {\n this.guardBuffer.push(event);\n // Flush guard events alongside regular events\n }\n\n async flush(): Promise<void> {\n if ((this.buffer.length === 0 && this.guardBuffer.length === 0) || this.flushing) return;\n\n this.flushing = true;\n\n // Flush cost events\n if (this.buffer.length > 0) {\n const batch = this.buffer.splice(0, this.batchSize);\n try {\n const response = await fetch(`${this.endpoint}/v1/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ batch }),\n });\n if (!response.ok) {\n this.buffer.unshift(...batch);\n }\n } catch {\n this.buffer.unshift(...batch);\n }\n }\n\n // Flush guard events\n if (this.guardBuffer.length > 0) {\n const guardBatch = this.guardBuffer.splice(0, this.batchSize);\n try {\n await fetch(`${this.endpoint}/v1/guard-events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ batch: guardBatch }),\n });\n } catch {\n // Fire-and-forget for guard events\n }\n }\n\n this.flushing = false;\n }\n\n async destroy(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n\n private startAutoFlush(): void {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushIntervalMs);\n\n if (this.timer && typeof this.timer === 'object' && 'unref' in this.timer) {\n (this.timer as { unref: () => void }).unref();\n }\n }\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var m={"gpt-4o":128e3,"gpt-4o-mini":128e3,"gpt-4.1":1e6,"gpt-4.1-mini":1e6,o1:2e5,"o3-mini":2e5,"claude-sonnet-4-20250514":2e5,"claude-haiku-4-5-20251001":2e5,"claude-opus-4-20250514":2e5,"claude-sonnet-4":2e5,"claude-haiku-4":2e5,"claude-opus-4":2e5};function _(t){if(typeof t!="string"){let e=typeof t=="object"?JSON.stringify(t):String(t??"");return Math.ceil(e.length/4)}return Math.ceil(t.length/4)}function f(t,e){let r=x(e),o=0,n=0,i=0;for(let a of t){let u=_(a.content);a.role==="system"?o+=u:a.role==="tool"?i+=u:n+=u;}let s=o+n+i;return {estimatedInputTokens:s,modelContextLimit:r,utilizationPercent:r>0?s/r:0,messageCount:t.length,systemPromptTokens:o,conversationTokens:n,toolResultTokens:i}}function x(t){if(m[t]!==void 0)return m[t];for(let[e,r]of Object.entries(m))if(t.startsWith(e))return r;return 128e3}function c(t,e){let r=0,o=t.inputTokens-(t.cachedTokens??0);if(o>0&&(r+=h(o*e.inputPricePerMToken,1e6)),t.cachedTokens&&t.cachedTokens>0){let n=e.cachedInputPricePerMToken??e.inputPricePerMToken;r+=h(t.cachedTokens*n,1e6);}if(t.outputTokens>0&&(r+=h(t.outputTokens*e.outputPricePerMToken,1e6)),t.reasoningTokens&&t.reasoningTokens>0){let n=e.reasoningPricePerMToken??e.outputPricePerMToken;r+=h(t.reasoningTokens*n,1e6);}return r}function h(t,e){let r=Math.trunc(t/e);return (t-r*e)*2>=e?r+1:r}var M={openai:{"gpt-4o":{inputPricePerMToken:25e5,outputPricePerMToken:1e7,cachedInputPricePerMToken:125e4},"gpt-4o-mini":{inputPricePerMToken:15e4,outputPricePerMToken:6e5,cachedInputPricePerMToken:75e3},"gpt-4.1":{inputPricePerMToken:2e6,outputPricePerMToken:8e6,cachedInputPricePerMToken:5e5},"gpt-4.1-mini":{inputPricePerMToken:4e5,outputPricePerMToken:16e5,cachedInputPricePerMToken:1e5},o1:{inputPricePerMToken:15e6,outputPricePerMToken:6e7,reasoningPricePerMToken:6e7,cachedInputPricePerMToken:75e5},"o3-mini":{inputPricePerMToken:11e5,outputPricePerMToken:44e5,reasoningPricePerMToken:44e5,cachedInputPricePerMToken:55e4}},anthropic:{"claude-sonnet-4-20250514":{inputPricePerMToken:3e6,outputPricePerMToken:15e6,cachedInputPricePerMToken:3e5},"claude-haiku-4-5-20251001":{inputPricePerMToken:8e5,outputPricePerMToken:4e6,cachedInputPricePerMToken:8e4},"claude-opus-4-20250514":{inputPricePerMToken:15e6,outputPricePerMToken:75e6,cachedInputPricePerMToken:15e5}}};function l(t,e){
|
|
1
|
+
var m={"gpt-4o":128e3,"gpt-4o-mini":128e3,"gpt-4.1":1e6,"gpt-4.1-mini":1e6,o1:2e5,"o3-mini":2e5,"claude-sonnet-4-20250514":2e5,"claude-haiku-4-5-20251001":2e5,"claude-opus-4-20250514":2e5,"claude-sonnet-4":2e5,"claude-haiku-4":2e5,"claude-opus-4":2e5};function _(t){if(typeof t!="string"){let e=typeof t=="object"?JSON.stringify(t):String(t??"");return Math.ceil(e.length/4)}return Math.ceil(t.length/4)}function f(t,e){let r=x(e),o=0,n=0,i=0;for(let a of t){let u=_(a.content);a.role==="system"?o+=u:a.role==="tool"?i+=u:n+=u;}let s=o+n+i;return {estimatedInputTokens:s,modelContextLimit:r,utilizationPercent:r>0?s/r:0,messageCount:t.length,systemPromptTokens:o,conversationTokens:n,toolResultTokens:i}}function x(t){if(m[t]!==void 0)return m[t];for(let[e,r]of Object.entries(m))if(t.startsWith(e))return r;return 128e3}function c(t,e){let r=0,o=t.inputTokens-(t.cachedTokens??0);if(o>0&&(r+=h(o*e.inputPricePerMToken,1e6)),t.cachedTokens&&t.cachedTokens>0){let n=e.cachedInputPricePerMToken??e.inputPricePerMToken;r+=h(t.cachedTokens*n,1e6);}if(t.outputTokens>0&&(r+=h(t.outputTokens*e.outputPricePerMToken,1e6)),t.reasoningTokens&&t.reasoningTokens>0){let n=e.reasoningPricePerMToken??e.outputPricePerMToken;r+=h(t.reasoningTokens*n,1e6);}return r}function h(t,e){let r=Math.trunc(t/e);return (t-r*e)*2>=e?r+1:r}var M={openai:{"gpt-4o":{inputPricePerMToken:25e5,outputPricePerMToken:1e7,cachedInputPricePerMToken:125e4},"gpt-4o-mini":{inputPricePerMToken:15e4,outputPricePerMToken:6e5,cachedInputPricePerMToken:75e3},"gpt-4.1":{inputPricePerMToken:2e6,outputPricePerMToken:8e6,cachedInputPricePerMToken:5e5},"gpt-4.1-mini":{inputPricePerMToken:4e5,outputPricePerMToken:16e5,cachedInputPricePerMToken:1e5},o1:{inputPricePerMToken:15e6,outputPricePerMToken:6e7,reasoningPricePerMToken:6e7,cachedInputPricePerMToken:75e5},"o3-mini":{inputPricePerMToken:11e5,outputPricePerMToken:44e5,reasoningPricePerMToken:44e5,cachedInputPricePerMToken:55e4}},anthropic:{"claude-sonnet-4-20250514":{inputPricePerMToken:3e6,outputPricePerMToken:15e6,cachedInputPricePerMToken:3e5},"claude-haiku-4-5-20251001":{inputPricePerMToken:8e5,outputPricePerMToken:4e6,cachedInputPricePerMToken:8e4},"claude-opus-4-20250514":{inputPricePerMToken:15e6,outputPricePerMToken:75e6,cachedInputPricePerMToken:15e5}}};function l(t,e){let r=M[t];if(r){if(r[e])return r[e];for(let o of Object.keys(r))if(e.startsWith(o))return r[o]}}var d=class extends Error{rule;current;threshold;suggestion;constructor(e,r){super(`NeuraMeter guard: ${e.ruleType} exceeded (${e.currentValue} > ${e.threshold})`),this.name="NeuraMeterGuardError",this.rule=e.ruleType,this.current=e.currentValue,this.threshold=e.threshold,this.suggestion=r;}};function k(t,e,r){let o=t.mode??"notify",n=[],i=f(e.messages,e.model);if(t.maxInputTokens&&i.estimatedInputTokens>t.maxInputTokens&&n.push({ruleType:"input_tokens",currentValue:i.estimatedInputTokens,threshold:t.maxInputTokens,isHard:false}),t.maxInputTokensHard&&i.estimatedInputTokens>t.maxInputTokensHard&&n.push({ruleType:"input_tokens",currentValue:i.estimatedInputTokens,threshold:t.maxInputTokensHard,isHard:true}),t.maxContextUtilization&&i.utilizationPercent>t.maxContextUtilization&&n.push({ruleType:"context_utilization",currentValue:i.utilizationPercent,threshold:t.maxContextUtilization,isHard:false}),t.maxContextUtilizationHard&&i.utilizationPercent>t.maxContextUtilizationHard&&n.push({ruleType:"context_utilization",currentValue:i.utilizationPercent,threshold:t.maxContextUtilizationHard,isHard:true}),t.maxCostPerCall||t.maxCostPerCallHard){let u=l(e.provider,e.model);if(u){let g=c({inputTokens:i.estimatedInputTokens,outputTokens:0},u)/1e6;t.maxCostPerCall&&g>t.maxCostPerCall&&n.push({ruleType:"cost_per_call",currentValue:g,threshold:t.maxCostPerCall,isHard:false}),t.maxCostPerCallHard&&g>t.maxCostPerCallHard&&n.push({ruleType:"cost_per_call",currentValue:g,threshold:t.maxCostPerCallHard,isHard:true});}}t.maxCostPerHour&&r!==void 0&&r>t.maxCostPerHour&&n.push({ruleType:"cost_per_hour",currentValue:r,threshold:t.maxCostPerHour,isHard:o==="block"});let s=C(n,i);if(n.length===0)return {decision:"allow",triggeredRules:n,contextAnalysis:i,suggestion:s};let a=n.some(u=>u.isHard);switch(o){case "notify":return {decision:"notify",triggeredRules:n,contextAnalysis:i,suggestion:s};case "block":return a?{decision:"block",triggeredRules:n,contextAnalysis:i,suggestion:s}:{decision:"notify",triggeredRules:n,contextAnalysis:i,suggestion:s};case "auto-optimize":return {decision:"notify",triggeredRules:n,contextAnalysis:i,suggestion:s};default:return {decision:"allow",triggeredRules:n,contextAnalysis:i,suggestion:s}}}function C(t,e){if(t.length===0)return "";let r=[];for(let o of t)switch(o.ruleType){case "context_utilization":if(e.conversationTokens>e.systemPromptTokens){let n=Math.round(e.conversationTokens/e.estimatedInputTokens*100);r.push(`Summarize conversation history to save ~${n}% of input tokens`);}break;case "input_tokens":r.push(`Reduce input tokens from ${e.estimatedInputTokens.toLocaleString()} to under ${o.threshold.toLocaleString()}`);break;case "cost_per_call":r.push("Consider using a cheaper model (e.g., gpt-4o-mini or claude-haiku)");break;case "cost_per_hour":r.push(`Hourly cost limit exceeded ($${o.currentValue.toFixed(2)} > $${o.threshold.toFixed(2)}). Throttle or pause agent calls`);break}return r.join(". ")||"Review guard thresholds or optimize context usage"}var p=class{traceId;agentName;customerId;taskName;tags;recordFn;constructor(e,r){this.traceId=crypto.randomUUID(),this.agentName=e.agentName,this.customerId=e.customerId,this.taskName=e.taskName,this.tags=e.tags,this.recordFn=r;}span(e){let r=crypto.randomUUID(),o=l(e.provider,e.model),n=o?c(e.usage,o):0;return this.recordFn({traceId:this.traceId,spanId:r,parentSpanId:e.parentSpanId,agentName:e.agentName??this.agentName,taskName:e.taskName??this.taskName,customerId:this.customerId,provider:e.provider,model:e.model,inputTokens:e.usage.inputTokens,outputTokens:e.usage.outputTokens,reasoningTokens:e.usage.reasoningTokens,cachedTokens:e.usage.cachedTokens,costMicrodollars:n,latencyMs:e.latencyMs,tags:{...this.tags,...e.tags}}),r}};function P(t,e){switch(t){case "context_utilization":return `${e.toFixed(1)}%`;case "cost_per_call":case "cost_per_hour":case "budget":return `$${e.toFixed(4)}`;case "input_tokens":return e.toLocaleString();default:return String(e)}}function b(t){return t.split("_").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}function v(t){let e=[{type:"header",text:{type:"plain_text",text:":warning: NeuraMeter Guard Alert",emoji:true}},{type:"section",fields:[{type:"mrkdwn",text:`*Agent:*
|
|
2
2
|
${t.agentName}`},{type:"mrkdwn",text:`*Rule Triggered:*
|
|
3
3
|
${b(t.ruleType)}`},{type:"mrkdwn",text:`*Current Value:*
|
|
4
4
|
${P(t.ruleType,t.currentValue)}`},{type:"mrkdwn",text:`*Threshold:*
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.ts","../src/cost.ts","../src/pricing.ts","../src/guards.ts","../src/trace.ts","../src/slack.ts","../src/meter.ts"],"names":["MODEL_CONTEXT_LIMITS","estimateTokens","content","str","analyzeContext","messages","model","modelLimit","getModelContextLimit","systemTokens","conversationTokens","toolResultTokens","msg","tokens","total","key","limit","calculateCostMicrodollars","usage","pricing","totalMicrodollars","effectiveInputTokens","integerDivRound","cachedPrice","reasoningPrice","numerator","denominator","quotient","PRICING","getModelPricing","provider","NeuraMeterGuardError","rule","suggestion","checkGuards","config","params","hourlyCostDollars","mode","triggeredRules","contextAnalysis","estimatedDollars","generateSuggestion","hasHardViolation","r","rules","ctx","parts","savingPct","Trace","opts","recordFn","spanId","costMicrodollars","formatValue","ruleType","value","formatRuleType","word","buildSlackPayload","message","blocks","sendSlackNotification","webhookUrl","payload","DEFAULT_BATCH_SIZE","DEFAULT_FLUSH_INTERVAL_MS","NeuraMeter","event","agentName","oneHourAgo","recent","e","entries","result","hardRule","primaryRule","optimizeEvent","tokensBefore","costBefore","fullEvent","batch","guardBatch"],"mappings":"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","file":"index.js","sourcesContent":["export interface ContextAnalysis {\n estimatedInputTokens: number;\n modelContextLimit: number;\n utilizationPercent: number;\n messageCount: number;\n systemPromptTokens: number;\n conversationTokens: number;\n toolResultTokens: number;\n}\n\nexport interface Message {\n role: string;\n content: string | unknown;\n}\n\n/**\n * Model context window limits (tokens).\n */\nexport const MODEL_CONTEXT_LIMITS: Record<string, number> = {\n 'gpt-4o': 128_000,\n 'gpt-4o-mini': 128_000,\n 'gpt-4.1': 1_000_000,\n 'gpt-4.1-mini': 1_000_000,\n 'o1': 200_000,\n 'o3-mini': 200_000,\n 'claude-sonnet-4-20250514': 200_000,\n 'claude-haiku-4-5-20251001': 200_000,\n 'claude-opus-4-20250514': 200_000,\n // Aliases for prefix matching\n 'claude-sonnet-4': 200_000,\n 'claude-haiku-4': 200_000,\n 'claude-opus-4': 200_000,\n};\n\n/**\n * Estimate token count from a string.\n * Uses a simple heuristic: ~4 chars per token for English,\n * ~1.5 chars per token for CJK/mixed content.\n * This is intentionally fast (<1ms) for SDK use.\n */\nexport function estimateTokens(content: string | unknown): number {\n if (typeof content !== 'string') {\n // For non-string content (e.g., tool_use blocks), serialize and estimate\n const str = typeof content === 'object' ? JSON.stringify(content) : String(content ?? '');\n return Math.ceil(str.length / 4);\n }\n // Simple heuristic: average of ~4 chars/token\n return Math.ceil(content.length / 4);\n}\n\n/**\n * Analyze context window utilization for a set of messages.\n */\nexport function analyzeContext(messages: Message[], model: string): ContextAnalysis {\n const modelLimit = getModelContextLimit(model);\n let systemTokens = 0;\n let conversationTokens = 0;\n let toolResultTokens = 0;\n\n for (const msg of messages) {\n const tokens = estimateTokens(msg.content);\n if (msg.role === 'system') {\n systemTokens += tokens;\n } else if (msg.role === 'tool') {\n toolResultTokens += tokens;\n } else {\n conversationTokens += tokens;\n }\n }\n\n const total = systemTokens + conversationTokens + toolResultTokens;\n\n return {\n estimatedInputTokens: total,\n modelContextLimit: modelLimit,\n utilizationPercent: modelLimit > 0 ? total / modelLimit : 0,\n messageCount: messages.length,\n systemPromptTokens: systemTokens,\n conversationTokens,\n toolResultTokens,\n };\n}\n\n/**\n * Get model context limit with prefix matching fallback.\n */\nexport function getModelContextLimit(model: string): number {\n if (MODEL_CONTEXT_LIMITS[model] !== undefined) {\n return MODEL_CONTEXT_LIMITS[model]!;\n }\n // Prefix match for versioned model names\n for (const [key, limit] of Object.entries(MODEL_CONTEXT_LIMITS)) {\n if (model.startsWith(key)) return limit;\n }\n return 128_000; // Default fallback\n}\n","import type { ModelPricing, TokenUsage } from './types';\n\n/**\n * Calculate cost in microdollars using integer arithmetic only.\n *\n * Pricing values are stored as microdollars per million tokens.\n * Formula: tokens * pricePerMToken / 1_000_000\n * (result is already in microdollars since price is in microdollars)\n *\n * To avoid floating-point, we compute:\n * cost = Math.round(tokens * pricePerMToken / 1_000_000)\n *\n * Since pricePerMToken is already an integer (microdollars per 1M tokens),\n * and tokens is an integer, the only division is by 1_000_000.\n * We use Math.round to get the nearest integer microdollar.\n */\nexport function calculateCostMicrodollars(\n usage: TokenUsage,\n pricing: ModelPricing,\n): number {\n let totalMicrodollars = 0;\n\n // Input tokens cost\n const effectiveInputTokens = usage.inputTokens - (usage.cachedTokens ?? 0);\n if (effectiveInputTokens > 0) {\n totalMicrodollars += integerDivRound(\n effectiveInputTokens * pricing.inputPricePerMToken,\n 1_000_000,\n );\n }\n\n // Cached tokens cost (discounted rate)\n if (usage.cachedTokens && usage.cachedTokens > 0) {\n const cachedPrice = pricing.cachedInputPricePerMToken ?? pricing.inputPricePerMToken;\n totalMicrodollars += integerDivRound(\n usage.cachedTokens * cachedPrice,\n 1_000_000,\n );\n }\n\n // Output tokens cost\n if (usage.outputTokens > 0) {\n totalMicrodollars += integerDivRound(\n usage.outputTokens * pricing.outputPricePerMToken,\n 1_000_000,\n );\n }\n\n // Reasoning tokens cost\n if (usage.reasoningTokens && usage.reasoningTokens > 0) {\n const reasoningPrice = pricing.reasoningPricePerMToken ?? pricing.outputPricePerMToken;\n totalMicrodollars += integerDivRound(\n usage.reasoningTokens * reasoningPrice,\n 1_000_000,\n );\n }\n\n return totalMicrodollars;\n}\n\n/**\n * Integer division with rounding (avoids floating-point).\n * Computes Math.round(numerator / denominator) using only integer ops.\n */\nfunction integerDivRound(numerator: number, denominator: number): number {\n const quotient = Math.trunc(numerator / denominator);\n const remainder = numerator - quotient * denominator;\n // Round: if remainder >= half the denominator, round up\n if (remainder * 2 >= denominator) {\n return quotient + 1;\n }\n return quotient;\n}\n","import type { ModelPricing } from './types';\n\n/**\n * Built-in pricing data — microdollars per million tokens.\n * e.g. $2.50 per 1M tokens = 2_500_000 microdollars per 1M tokens\n */\nconst PRICING: Record<string, Record<string, ModelPricing>> = {\n openai: {\n 'gpt-4o': {\n inputPricePerMToken: 2_500_000,\n outputPricePerMToken: 10_000_000,\n cachedInputPricePerMToken: 1_250_000,\n },\n 'gpt-4o-mini': {\n inputPricePerMToken: 150_000,\n outputPricePerMToken: 600_000,\n cachedInputPricePerMToken: 75_000,\n },\n 'gpt-4.1': {\n inputPricePerMToken: 2_000_000,\n outputPricePerMToken: 8_000_000,\n cachedInputPricePerMToken: 500_000,\n },\n 'gpt-4.1-mini': {\n inputPricePerMToken: 400_000,\n outputPricePerMToken: 1_600_000,\n cachedInputPricePerMToken: 100_000,\n },\n o1: {\n inputPricePerMToken: 15_000_000,\n outputPricePerMToken: 60_000_000,\n reasoningPricePerMToken: 60_000_000,\n cachedInputPricePerMToken: 7_500_000,\n },\n 'o3-mini': {\n inputPricePerMToken: 1_100_000,\n outputPricePerMToken: 4_400_000,\n reasoningPricePerMToken: 4_400_000,\n cachedInputPricePerMToken: 550_000,\n },\n },\n anthropic: {\n 'claude-sonnet-4-20250514': {\n inputPricePerMToken: 3_000_000,\n outputPricePerMToken: 15_000_000,\n cachedInputPricePerMToken: 300_000,\n },\n 'claude-haiku-4-5-20251001': {\n inputPricePerMToken: 800_000,\n outputPricePerMToken: 4_000_000,\n cachedInputPricePerMToken: 80_000,\n },\n 'claude-opus-4-20250514': {\n inputPricePerMToken: 15_000_000,\n outputPricePerMToken: 75_000_000,\n cachedInputPricePerMToken: 1_500_000,\n },\n },\n};\n\nexport function getModelPricing(\n provider: string,\n model: string,\n): ModelPricing | undefined {\n return PRICING[provider]?.[model];\n}\n","import type { ContextAnalysis, Message } from './context';\nimport { analyzeContext } from './context';\nimport { calculateCostMicrodollars } from './cost';\nimport { getModelPricing } from './pricing';\n\nexport type GuardMode = 'notify' | 'block' | 'auto-optimize';\nexport type GuardDecision = 'allow' | 'notify' | 'block' | 'optimized';\n\nexport interface GuardsConfig {\n maxInputTokens?: number;\n maxInputTokensHard?: number;\n maxContextUtilization?: number;\n maxContextUtilizationHard?: number;\n maxCostPerCall?: number;\n maxCostPerCallHard?: number;\n maxCostPerHour?: number;\n mode?: GuardMode;\n notifySlackWebhook?: string;\n notifyDashboard?: boolean;\n onOptimize?: (event: OptimizeEvent) => Promise<OptimizeResult>;\n}\n\nexport interface TriggeredRule {\n ruleType: 'input_tokens' | 'cost_per_call' | 'cost_per_hour' | 'context_utilization' | 'budget';\n currentValue: number;\n threshold: number;\n isHard: boolean;\n}\n\nexport interface GuardCheckResult {\n decision: GuardDecision;\n triggeredRules: TriggeredRule[];\n contextAnalysis: ContextAnalysis | null;\n suggestion?: string;\n}\n\nexport interface GuardEvent {\n eventId: string;\n timestamp: string;\n agentName: string;\n guardMode: GuardMode;\n decision: GuardDecision;\n triggeredRules: TriggeredRule[];\n contextAnalysis?: ContextAnalysis;\n optimization?: {\n action: 'retry' | 'notify' | 'block';\n tokensBefore: number;\n tokensAfter?: number;\n costBefore: number;\n costAfter?: number;\n description?: string;\n };\n suggestion?: string;\n}\n\nexport interface OptimizeEvent {\n type: 'context_utilization' | 'cost_per_call' | 'input_tokens';\n suggestion: string;\n metrics: {\n messages?: Message[];\n model?: string;\n currentValue: number;\n threshold: number;\n };\n}\n\nexport interface OptimizeResult {\n action: 'retry' | 'notify' | 'block';\n messages?: Message[];\n model?: string;\n}\n\n/**\n * Error thrown when guard mode is 'block' and a hard limit is exceeded.\n */\nexport class NeuraMeterGuardError extends Error {\n readonly rule: string;\n readonly current: number;\n readonly threshold: number;\n readonly suggestion: string;\n\n constructor(rule: TriggeredRule, suggestion: string) {\n super(`NeuraMeter guard: ${rule.ruleType} exceeded (${rule.currentValue} > ${rule.threshold})`);\n this.name = 'NeuraMeterGuardError';\n this.rule = rule.ruleType;\n this.current = rule.currentValue;\n this.threshold = rule.threshold;\n this.suggestion = suggestion;\n }\n}\n\n/**\n * Check guard rules before an API call.\n * Returns the decision and any triggered rules.\n */\nexport function checkGuards(\n config: GuardsConfig,\n params: {\n messages: Message[];\n model: string;\n provider: string;\n agentName: string;\n },\n hourlyCostDollars?: number,\n): GuardCheckResult {\n const mode = config.mode ?? 'notify';\n const triggeredRules: TriggeredRule[] = [];\n\n // Context analysis\n const contextAnalysis = analyzeContext(params.messages, params.model);\n\n // Check input tokens\n if (config.maxInputTokens && contextAnalysis.estimatedInputTokens > config.maxInputTokens) {\n triggeredRules.push({\n ruleType: 'input_tokens',\n currentValue: contextAnalysis.estimatedInputTokens,\n threshold: config.maxInputTokens,\n isHard: false,\n });\n }\n if (config.maxInputTokensHard && contextAnalysis.estimatedInputTokens > config.maxInputTokensHard) {\n triggeredRules.push({\n ruleType: 'input_tokens',\n currentValue: contextAnalysis.estimatedInputTokens,\n threshold: config.maxInputTokensHard,\n isHard: true,\n });\n }\n\n // Check context utilization\n if (config.maxContextUtilization && contextAnalysis.utilizationPercent > config.maxContextUtilization) {\n triggeredRules.push({\n ruleType: 'context_utilization',\n currentValue: contextAnalysis.utilizationPercent,\n threshold: config.maxContextUtilization,\n isHard: false,\n });\n }\n if (config.maxContextUtilizationHard && contextAnalysis.utilizationPercent > config.maxContextUtilizationHard) {\n triggeredRules.push({\n ruleType: 'context_utilization',\n currentValue: contextAnalysis.utilizationPercent,\n threshold: config.maxContextUtilizationHard,\n isHard: true,\n });\n }\n\n // Check estimated cost per call\n if (config.maxCostPerCall || config.maxCostPerCallHard) {\n const pricing = getModelPricing(params.provider, params.model);\n if (pricing) {\n const estimatedCost = calculateCostMicrodollars(\n { inputTokens: contextAnalysis.estimatedInputTokens, outputTokens: 0 },\n pricing,\n );\n const estimatedDollars = estimatedCost / 1_000_000;\n\n if (config.maxCostPerCall && estimatedDollars > config.maxCostPerCall) {\n triggeredRules.push({\n ruleType: 'cost_per_call',\n currentValue: estimatedDollars,\n threshold: config.maxCostPerCall,\n isHard: false,\n });\n }\n if (config.maxCostPerCallHard && estimatedDollars > config.maxCostPerCallHard) {\n triggeredRules.push({\n ruleType: 'cost_per_call',\n currentValue: estimatedDollars,\n threshold: config.maxCostPerCallHard,\n isHard: true,\n });\n }\n }\n }\n\n // Check cost per hour\n if (config.maxCostPerHour && hourlyCostDollars !== undefined && hourlyCostDollars > config.maxCostPerHour) {\n triggeredRules.push({\n ruleType: 'cost_per_hour',\n currentValue: hourlyCostDollars,\n threshold: config.maxCostPerHour,\n isHard: mode === 'block',\n });\n }\n\n // Generate suggestion\n const suggestion = generateSuggestion(triggeredRules, contextAnalysis);\n\n // Determine decision based on mode\n if (triggeredRules.length === 0) {\n return { decision: 'allow', triggeredRules, contextAnalysis, suggestion };\n }\n\n const hasHardViolation = triggeredRules.some((r) => r.isHard);\n\n switch (mode) {\n case 'notify':\n return { decision: 'notify', triggeredRules, contextAnalysis, suggestion };\n\n case 'block':\n if (hasHardViolation) {\n return { decision: 'block', triggeredRules, contextAnalysis, suggestion };\n }\n return { decision: 'notify', triggeredRules, contextAnalysis, suggestion };\n\n case 'auto-optimize':\n return { decision: 'notify', triggeredRules, contextAnalysis, suggestion };\n\n default:\n return { decision: 'allow', triggeredRules, contextAnalysis, suggestion };\n }\n}\n\nfunction generateSuggestion(rules: TriggeredRule[], ctx: ContextAnalysis): string {\n if (rules.length === 0) return '';\n\n const parts: string[] = [];\n\n for (const rule of rules) {\n switch (rule.ruleType) {\n case 'context_utilization':\n if (ctx.conversationTokens > ctx.systemPromptTokens) {\n const savingPct = Math.round((ctx.conversationTokens / ctx.estimatedInputTokens) * 100);\n parts.push(`Summarize conversation history to save ~${savingPct}% of input tokens`);\n }\n break;\n case 'input_tokens':\n parts.push(`Reduce input tokens from ${ctx.estimatedInputTokens.toLocaleString()} to under ${rule.threshold.toLocaleString()}`);\n break;\n case 'cost_per_call':\n parts.push('Consider using a cheaper model (e.g., gpt-4o-mini or claude-haiku)');\n break;\n case 'cost_per_hour':\n parts.push(`Hourly cost limit exceeded ($${rule.currentValue.toFixed(2)} > $${rule.threshold.toFixed(2)}). Throttle or pause agent calls`);\n break;\n }\n }\n\n return parts.join('. ') || 'Review guard thresholds or optimize context usage';\n}\n","import type { CostEvent, Provider, TraceOptions, TokenUsage } from './types';\nimport { calculateCostMicrodollars } from './cost';\nimport { getModelPricing } from './pricing';\n\nexport class Trace {\n readonly traceId: string;\n private readonly agentName: string;\n private readonly customerId?: string;\n private readonly taskName?: string;\n private readonly tags?: Record<string, string>;\n private readonly recordFn: (event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId' | 'cost'>) => void;\n\n constructor(\n opts: TraceOptions,\n recordFn: (event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId' | 'cost'>) => void,\n ) {\n this.traceId = crypto.randomUUID();\n this.agentName = opts.agentName;\n this.customerId = opts.customerId;\n this.taskName = opts.taskName;\n this.tags = opts.tags;\n this.recordFn = recordFn;\n }\n\n span(opts: {\n provider: Provider;\n model: string;\n usage: TokenUsage;\n latencyMs: number;\n parentSpanId?: string;\n agentName?: string;\n taskName?: string;\n tags?: Record<string, string>;\n }): string {\n const spanId = crypto.randomUUID();\n const pricing = getModelPricing(opts.provider, opts.model);\n const costMicrodollars = pricing\n ? calculateCostMicrodollars(opts.usage, pricing)\n : 0;\n\n this.recordFn({\n traceId: this.traceId,\n spanId,\n parentSpanId: opts.parentSpanId,\n agentName: opts.agentName ?? this.agentName,\n taskName: opts.taskName ?? this.taskName,\n customerId: this.customerId,\n provider: opts.provider,\n model: opts.model,\n inputTokens: opts.usage.inputTokens,\n outputTokens: opts.usage.outputTokens,\n reasoningTokens: opts.usage.reasoningTokens,\n cachedTokens: opts.usage.cachedTokens,\n costMicrodollars,\n latencyMs: opts.latencyMs,\n tags: { ...this.tags, ...opts.tags },\n });\n\n return spanId;\n }\n}\n","/**\n * Slack notification utility for NeuraMeter guard alerts.\n * Sends Block Kit formatted messages via incoming webhooks.\n */\n\nexport interface SlackMessage {\n /** Plain text fallback */\n text: string;\n /** Name of the agent that triggered the guard */\n agentName: string;\n /** Type of guard rule triggered (e.g. 'context_utilization', 'input_tokens') */\n ruleType: string;\n /** Current value that exceeded the threshold */\n currentValue: number;\n /** Configured threshold that was exceeded */\n threshold: number;\n /** Optimization suggestion from the guard system */\n suggestion?: string;\n}\n\n/**\n * Format a value for display based on the rule type.\n */\nfunction formatValue(ruleType: string, value: number): string {\n switch (ruleType) {\n case 'context_utilization':\n return `${value.toFixed(1)}%`;\n case 'cost_per_call':\n case 'cost_per_hour':\n case 'budget':\n return `$${value.toFixed(4)}`;\n case 'input_tokens':\n return value.toLocaleString();\n default:\n return String(value);\n }\n}\n\n/**\n * Format a rule type string for human-readable display.\n */\nfunction formatRuleType(ruleType: string): string {\n return ruleType\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\n/**\n * Build a Slack Block Kit payload for a guard alert.\n */\nfunction buildSlackPayload(message: SlackMessage): object {\n const blocks: object[] = [\n {\n type: 'header',\n text: {\n type: 'plain_text',\n text: `:warning: NeuraMeter Guard Alert`,\n emoji: true,\n },\n },\n {\n type: 'section',\n fields: [\n {\n type: 'mrkdwn',\n text: `*Agent:*\\n${message.agentName}`,\n },\n {\n type: 'mrkdwn',\n text: `*Rule Triggered:*\\n${formatRuleType(message.ruleType)}`,\n },\n {\n type: 'mrkdwn',\n text: `*Current Value:*\\n${formatValue(message.ruleType, message.currentValue)}`,\n },\n {\n type: 'mrkdwn',\n text: `*Threshold:*\\n${formatValue(message.ruleType, message.threshold)}`,\n },\n ],\n },\n ];\n\n if (message.suggestion) {\n blocks.push({\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `*Suggestion:*\\n${message.suggestion}`,\n },\n });\n }\n\n blocks.push({\n type: 'context',\n elements: [\n {\n type: 'mrkdwn',\n text: `Sent by NeuraMeter at ${new Date().toISOString()}`,\n },\n ],\n });\n\n return {\n text: message.text,\n blocks,\n };\n}\n\n/**\n * Send a Slack notification via an incoming webhook.\n * Fire-and-forget: errors are silently caught and do not propagate.\n *\n * @param webhookUrl - Slack incoming webhook URL\n * @param message - Structured alert message\n */\nexport async function sendSlackNotification(\n webhookUrl: string,\n message: SlackMessage,\n): Promise<void> {\n try {\n const payload = buildSlackPayload(message);\n await fetch(webhookUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n } catch {\n // Fire-and-forget: silently ignore errors\n }\n}\n","import type { CostEvent, NeuraMeterConfig, TraceOptions } from './types';\nimport type { GuardsConfig, GuardCheckResult, GuardEvent, OptimizeEvent, OptimizeResult } from './guards';\nimport { checkGuards, NeuraMeterGuardError } from './guards';\nimport type { Message } from './context';\nimport { Trace } from './trace';\nimport { calculateCostMicrodollars } from './cost';\nimport { getModelPricing } from './pricing';\nimport { sendSlackNotification } from './slack';\n\nconst DEFAULT_BATCH_SIZE = 50;\nconst DEFAULT_FLUSH_INTERVAL_MS = 5_000;\n\n/** Tracks cost accumulated within a rolling 1-hour window per agent. */\ninterface HourlyCostEntry {\n costMicrodollars: number;\n timestamp: number;\n}\n\nexport class NeuraMeter {\n private readonly apiKey: string;\n private readonly projectId: string;\n private readonly endpoint: string;\n private readonly batchSize: number;\n private readonly flushIntervalMs: number;\n readonly guards: GuardsConfig | undefined;\n\n private buffer: CostEvent[] = [];\n private guardBuffer: GuardEvent[] = [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private timer: any = null;\n private flushing = false;\n\n /** Rolling hourly cost tracking per agent for maxCostPerHour guard */\n private hourlyCosts: Map<string, HourlyCostEntry[]> = new Map();\n\n /** Extracted from API key format: nm_{orgId}_{secret} */\n private readonly orgId: string;\n\n constructor(config: NeuraMeterConfig) {\n this.apiKey = config.apiKey;\n this.projectId = config.projectId;\n this.endpoint = config.endpoint ?? 'https://neurameter-ingestion.neurameter.workers.dev';\n this.batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\n this.flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n this.guards = config.guards;\n\n // Extract orgId from API key (format: nm_{orgId}_{secret})\n const parts = this.apiKey.split('_');\n this.orgId = parts.length >= 3 ? parts[1]! : 'unknown';\n\n this.startAutoFlush();\n }\n\n startTrace(opts: TraceOptions): Trace {\n return new Trace(opts, (event) => this.record(event));\n }\n\n /**\n * Get rolling hourly cost for an agent (in dollars).\n */\n getHourlyCostDollars(agentName: string): number {\n const oneHourAgo = Date.now() - 3_600_000;\n const entries = this.hourlyCosts.get(agentName) ?? [];\n // Prune old entries\n const recent = entries.filter((e) => e.timestamp > oneHourAgo);\n this.hourlyCosts.set(agentName, recent);\n const totalMicro = recent.reduce((s, e) => s + e.costMicrodollars, 0);\n return totalMicro / 1_000_000;\n }\n\n /**\n * Track cost for hourly rate limiting.\n */\n private trackHourlyCost(agentName: string, costMicrodollars: number): void {\n const entries = this.hourlyCosts.get(agentName) ?? [];\n entries.push({ costMicrodollars, timestamp: Date.now() });\n this.hourlyCosts.set(agentName, entries);\n }\n\n /**\n * Check guard rules before an API call.\n * Returns the check result. In block mode, may throw NeuraMeterGuardError.\n */\n checkGuards(params: {\n messages: Message[];\n model: string;\n provider: string;\n agentName: string;\n }): GuardCheckResult | null {\n if (!this.guards) return null;\n\n const hourlyCostDollars = this.getHourlyCostDollars(params.agentName);\n const result = checkGuards(this.guards, params, hourlyCostDollars);\n\n // Record guard event (async, fire-and-forget)\n if (result.triggeredRules.length > 0) {\n this.recordGuardEvent({\n eventId: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n agentName: params.agentName,\n guardMode: this.guards.mode ?? 'notify',\n decision: result.decision,\n triggeredRules: result.triggeredRules,\n contextAnalysis: result.contextAnalysis ?? undefined,\n suggestion: result.suggestion,\n });\n\n // Send Slack notification if configured (async, fire-and-forget)\n if (this.guards.notifySlackWebhook) {\n for (const rule of result.triggeredRules) {\n void sendSlackNotification(this.guards.notifySlackWebhook, {\n text: `NeuraMeter guard triggered: ${rule.ruleType} for agent \"${params.agentName}\" (${rule.currentValue} > ${rule.threshold})`,\n agentName: params.agentName,\n ruleType: rule.ruleType,\n currentValue: rule.currentValue,\n threshold: rule.threshold,\n suggestion: result.suggestion,\n });\n }\n }\n }\n\n // In block mode with hard violation, throw\n if (result.decision === 'block') {\n const hardRule = result.triggeredRules.find((r) => r.isHard);\n if (hardRule) {\n throw new NeuraMeterGuardError(hardRule, result.suggestion ?? '');\n }\n }\n\n return result;\n }\n\n /**\n * Run auto-optimize flow: calls the onOptimize callback and returns the result.\n * Used by SDK wrappers when mode is 'auto-optimize' and thresholds are exceeded.\n */\n async runAutoOptimize(params: {\n guardResult: GuardCheckResult;\n messages: Message[];\n model: string;\n agentName: string;\n }): Promise<OptimizeResult | null> {\n if (!this.guards?.onOptimize || params.guardResult.triggeredRules.length === 0) {\n return null;\n }\n\n const primaryRule = params.guardResult.triggeredRules[0]!;\n const optimizeEvent: OptimizeEvent = {\n type: primaryRule.ruleType as OptimizeEvent['type'],\n suggestion: params.guardResult.suggestion ?? '',\n metrics: {\n messages: params.messages,\n model: params.model,\n currentValue: primaryRule.currentValue,\n threshold: primaryRule.threshold,\n },\n };\n\n try {\n const result = await this.guards.onOptimize(optimizeEvent);\n\n // Record optimization in guard event\n const pricing = getModelPricing(\n 'openai', // Best effort — wrappers will provide actual provider\n result.model ?? params.model,\n );\n const tokensBefore = params.guardResult.contextAnalysis?.estimatedInputTokens ?? 0;\n const costBefore = pricing\n ? calculateCostMicrodollars({ inputTokens: tokensBefore, outputTokens: 0 }, pricing) / 1_000_000\n : 0;\n\n this.recordGuardEvent({\n eventId: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n agentName: params.agentName,\n guardMode: 'auto-optimize',\n decision: result.action === 'retry' ? 'optimized' : result.action === 'block' ? 'block' : 'notify',\n triggeredRules: params.guardResult.triggeredRules,\n contextAnalysis: params.guardResult.contextAnalysis ?? undefined,\n optimization: {\n action: result.action,\n tokensBefore,\n costBefore,\n description: params.guardResult.suggestion,\n },\n suggestion: params.guardResult.suggestion,\n });\n\n return result;\n } catch {\n // If onOptimize fails, fall through to notify\n return { action: 'notify' };\n }\n }\n\n record(event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId' | 'cost'>): void {\n const fullEvent: CostEvent = {\n ...event,\n eventId: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n orgId: this.orgId,\n projectId: this.projectId,\n cost: event.costMicrodollars / 1_000_000,\n };\n\n this.buffer.push(fullEvent);\n\n // Track hourly cost for rate limiting\n if (event.agentName && event.costMicrodollars > 0) {\n this.trackHourlyCost(event.agentName, event.costMicrodollars);\n }\n\n if (this.buffer.length >= this.batchSize) {\n void this.flush();\n }\n }\n\n private recordGuardEvent(event: GuardEvent): void {\n this.guardBuffer.push(event);\n // Flush guard events alongside regular events\n }\n\n async flush(): Promise<void> {\n if ((this.buffer.length === 0 && this.guardBuffer.length === 0) || this.flushing) return;\n\n this.flushing = true;\n\n // Flush cost events\n if (this.buffer.length > 0) {\n const batch = this.buffer.splice(0, this.batchSize);\n try {\n const response = await fetch(`${this.endpoint}/v1/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ batch }),\n });\n if (!response.ok) {\n this.buffer.unshift(...batch);\n }\n } catch {\n this.buffer.unshift(...batch);\n }\n }\n\n // Flush guard events\n if (this.guardBuffer.length > 0) {\n const guardBatch = this.guardBuffer.splice(0, this.batchSize);\n try {\n await fetch(`${this.endpoint}/v1/guard-events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ batch: guardBatch }),\n });\n } catch {\n // Fire-and-forget for guard events\n }\n }\n\n this.flushing = false;\n }\n\n async destroy(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n\n private startAutoFlush(): void {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushIntervalMs);\n\n if (this.timer && typeof this.timer === 'object' && 'unref' in this.timer) {\n (this.timer as { unref: () => void }).unref();\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/cost.ts","../src/pricing.ts","../src/guards.ts","../src/trace.ts","../src/slack.ts","../src/meter.ts"],"names":["MODEL_CONTEXT_LIMITS","estimateTokens","content","str","analyzeContext","messages","model","modelLimit","getModelContextLimit","systemTokens","conversationTokens","toolResultTokens","msg","tokens","total","key","limit","calculateCostMicrodollars","usage","pricing","totalMicrodollars","effectiveInputTokens","integerDivRound","cachedPrice","reasoningPrice","numerator","denominator","quotient","PRICING","getModelPricing","provider","providerPricing","NeuraMeterGuardError","rule","suggestion","checkGuards","config","params","hourlyCostDollars","mode","triggeredRules","contextAnalysis","estimatedDollars","generateSuggestion","hasHardViolation","r","rules","ctx","parts","savingPct","Trace","opts","recordFn","spanId","costMicrodollars","formatValue","ruleType","value","formatRuleType","word","buildSlackPayload","message","blocks","sendSlackNotification","webhookUrl","payload","DEFAULT_BATCH_SIZE","DEFAULT_FLUSH_INTERVAL_MS","NeuraMeter","event","agentName","oneHourAgo","recent","e","entries","result","hardRule","primaryRule","optimizeEvent","tokensBefore","costBefore","fullEvent","batch","guardBatch"],"mappings":"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","file":"index.js","sourcesContent":["export interface ContextAnalysis {\n estimatedInputTokens: number;\n modelContextLimit: number;\n utilizationPercent: number;\n messageCount: number;\n systemPromptTokens: number;\n conversationTokens: number;\n toolResultTokens: number;\n}\n\nexport interface Message {\n role: string;\n content: string | unknown;\n}\n\n/**\n * Model context window limits (tokens).\n */\nexport const MODEL_CONTEXT_LIMITS: Record<string, number> = {\n 'gpt-4o': 128_000,\n 'gpt-4o-mini': 128_000,\n 'gpt-4.1': 1_000_000,\n 'gpt-4.1-mini': 1_000_000,\n 'o1': 200_000,\n 'o3-mini': 200_000,\n 'claude-sonnet-4-20250514': 200_000,\n 'claude-haiku-4-5-20251001': 200_000,\n 'claude-opus-4-20250514': 200_000,\n // Aliases for prefix matching\n 'claude-sonnet-4': 200_000,\n 'claude-haiku-4': 200_000,\n 'claude-opus-4': 200_000,\n};\n\n/**\n * Estimate token count from a string.\n * Uses a simple heuristic: ~4 chars per token for English,\n * ~1.5 chars per token for CJK/mixed content.\n * This is intentionally fast (<1ms) for SDK use.\n */\nexport function estimateTokens(content: string | unknown): number {\n if (typeof content !== 'string') {\n // For non-string content (e.g., tool_use blocks), serialize and estimate\n const str = typeof content === 'object' ? JSON.stringify(content) : String(content ?? '');\n return Math.ceil(str.length / 4);\n }\n // Simple heuristic: average of ~4 chars/token\n return Math.ceil(content.length / 4);\n}\n\n/**\n * Analyze context window utilization for a set of messages.\n */\nexport function analyzeContext(messages: Message[], model: string): ContextAnalysis {\n const modelLimit = getModelContextLimit(model);\n let systemTokens = 0;\n let conversationTokens = 0;\n let toolResultTokens = 0;\n\n for (const msg of messages) {\n const tokens = estimateTokens(msg.content);\n if (msg.role === 'system') {\n systemTokens += tokens;\n } else if (msg.role === 'tool') {\n toolResultTokens += tokens;\n } else {\n conversationTokens += tokens;\n }\n }\n\n const total = systemTokens + conversationTokens + toolResultTokens;\n\n return {\n estimatedInputTokens: total,\n modelContextLimit: modelLimit,\n utilizationPercent: modelLimit > 0 ? total / modelLimit : 0,\n messageCount: messages.length,\n systemPromptTokens: systemTokens,\n conversationTokens,\n toolResultTokens,\n };\n}\n\n/**\n * Get model context limit with prefix matching fallback.\n */\nexport function getModelContextLimit(model: string): number {\n if (MODEL_CONTEXT_LIMITS[model] !== undefined) {\n return MODEL_CONTEXT_LIMITS[model]!;\n }\n // Prefix match for versioned model names\n for (const [key, limit] of Object.entries(MODEL_CONTEXT_LIMITS)) {\n if (model.startsWith(key)) return limit;\n }\n return 128_000; // Default fallback\n}\n","import type { ModelPricing, TokenUsage } from './types';\n\n/**\n * Calculate cost in microdollars using integer arithmetic only.\n *\n * Pricing values are stored as microdollars per million tokens.\n * Formula: tokens * pricePerMToken / 1_000_000\n * (result is already in microdollars since price is in microdollars)\n *\n * To avoid floating-point, we compute:\n * cost = Math.round(tokens * pricePerMToken / 1_000_000)\n *\n * Since pricePerMToken is already an integer (microdollars per 1M tokens),\n * and tokens is an integer, the only division is by 1_000_000.\n * We use Math.round to get the nearest integer microdollar.\n */\nexport function calculateCostMicrodollars(\n usage: TokenUsage,\n pricing: ModelPricing,\n): number {\n let totalMicrodollars = 0;\n\n // Input tokens cost\n const effectiveInputTokens = usage.inputTokens - (usage.cachedTokens ?? 0);\n if (effectiveInputTokens > 0) {\n totalMicrodollars += integerDivRound(\n effectiveInputTokens * pricing.inputPricePerMToken,\n 1_000_000,\n );\n }\n\n // Cached tokens cost (discounted rate)\n if (usage.cachedTokens && usage.cachedTokens > 0) {\n const cachedPrice = pricing.cachedInputPricePerMToken ?? pricing.inputPricePerMToken;\n totalMicrodollars += integerDivRound(\n usage.cachedTokens * cachedPrice,\n 1_000_000,\n );\n }\n\n // Output tokens cost\n if (usage.outputTokens > 0) {\n totalMicrodollars += integerDivRound(\n usage.outputTokens * pricing.outputPricePerMToken,\n 1_000_000,\n );\n }\n\n // Reasoning tokens cost\n if (usage.reasoningTokens && usage.reasoningTokens > 0) {\n const reasoningPrice = pricing.reasoningPricePerMToken ?? pricing.outputPricePerMToken;\n totalMicrodollars += integerDivRound(\n usage.reasoningTokens * reasoningPrice,\n 1_000_000,\n );\n }\n\n return totalMicrodollars;\n}\n\n/**\n * Integer division with rounding (avoids floating-point).\n * Computes Math.round(numerator / denominator) using only integer ops.\n */\nfunction integerDivRound(numerator: number, denominator: number): number {\n const quotient = Math.trunc(numerator / denominator);\n const remainder = numerator - quotient * denominator;\n // Round: if remainder >= half the denominator, round up\n if (remainder * 2 >= denominator) {\n return quotient + 1;\n }\n return quotient;\n}\n","import type { ModelPricing } from './types';\n\n/**\n * Built-in pricing data — microdollars per million tokens.\n * e.g. $2.50 per 1M tokens = 2_500_000 microdollars per 1M tokens\n */\nconst PRICING: Record<string, Record<string, ModelPricing>> = {\n openai: {\n 'gpt-4o': {\n inputPricePerMToken: 2_500_000,\n outputPricePerMToken: 10_000_000,\n cachedInputPricePerMToken: 1_250_000,\n },\n 'gpt-4o-mini': {\n inputPricePerMToken: 150_000,\n outputPricePerMToken: 600_000,\n cachedInputPricePerMToken: 75_000,\n },\n 'gpt-4.1': {\n inputPricePerMToken: 2_000_000,\n outputPricePerMToken: 8_000_000,\n cachedInputPricePerMToken: 500_000,\n },\n 'gpt-4.1-mini': {\n inputPricePerMToken: 400_000,\n outputPricePerMToken: 1_600_000,\n cachedInputPricePerMToken: 100_000,\n },\n o1: {\n inputPricePerMToken: 15_000_000,\n outputPricePerMToken: 60_000_000,\n reasoningPricePerMToken: 60_000_000,\n cachedInputPricePerMToken: 7_500_000,\n },\n 'o3-mini': {\n inputPricePerMToken: 1_100_000,\n outputPricePerMToken: 4_400_000,\n reasoningPricePerMToken: 4_400_000,\n cachedInputPricePerMToken: 550_000,\n },\n },\n anthropic: {\n 'claude-sonnet-4-20250514': {\n inputPricePerMToken: 3_000_000,\n outputPricePerMToken: 15_000_000,\n cachedInputPricePerMToken: 300_000,\n },\n 'claude-haiku-4-5-20251001': {\n inputPricePerMToken: 800_000,\n outputPricePerMToken: 4_000_000,\n cachedInputPricePerMToken: 80_000,\n },\n 'claude-opus-4-20250514': {\n inputPricePerMToken: 15_000_000,\n outputPricePerMToken: 75_000_000,\n cachedInputPricePerMToken: 1_500_000,\n },\n },\n};\n\nexport function getModelPricing(\n provider: string,\n model: string,\n): ModelPricing | undefined {\n const providerPricing = PRICING[provider];\n if (!providerPricing) return undefined;\n\n // Exact match first\n if (providerPricing[model]) return providerPricing[model];\n\n // Prefix match: e.g. \"gpt-4o-mini-2024-07-18\" → \"gpt-4o-mini\"\n for (const key of Object.keys(providerPricing)) {\n if (model.startsWith(key)) return providerPricing[key];\n }\n\n return undefined;\n}\n","import type { ContextAnalysis, Message } from './context';\nimport { analyzeContext } from './context';\nimport { calculateCostMicrodollars } from './cost';\nimport { getModelPricing } from './pricing';\n\nexport type GuardMode = 'notify' | 'block' | 'auto-optimize';\nexport type GuardDecision = 'allow' | 'notify' | 'block' | 'optimized';\n\nexport interface GuardsConfig {\n maxInputTokens?: number;\n maxInputTokensHard?: number;\n maxContextUtilization?: number;\n maxContextUtilizationHard?: number;\n maxCostPerCall?: number;\n maxCostPerCallHard?: number;\n maxCostPerHour?: number;\n mode?: GuardMode;\n notifySlackWebhook?: string;\n notifyDashboard?: boolean;\n onOptimize?: (event: OptimizeEvent) => Promise<OptimizeResult>;\n}\n\nexport interface TriggeredRule {\n ruleType: 'input_tokens' | 'cost_per_call' | 'cost_per_hour' | 'context_utilization' | 'budget';\n currentValue: number;\n threshold: number;\n isHard: boolean;\n}\n\nexport interface GuardCheckResult {\n decision: GuardDecision;\n triggeredRules: TriggeredRule[];\n contextAnalysis: ContextAnalysis | null;\n suggestion?: string;\n}\n\nexport interface GuardEvent {\n eventId: string;\n timestamp: string;\n agentName: string;\n guardMode: GuardMode;\n decision: GuardDecision;\n triggeredRules: TriggeredRule[];\n contextAnalysis?: ContextAnalysis;\n optimization?: {\n action: 'retry' | 'notify' | 'block';\n tokensBefore: number;\n tokensAfter?: number;\n costBefore: number;\n costAfter?: number;\n description?: string;\n };\n suggestion?: string;\n}\n\nexport interface OptimizeEvent {\n type: 'context_utilization' | 'cost_per_call' | 'input_tokens';\n suggestion: string;\n metrics: {\n messages?: Message[];\n model?: string;\n currentValue: number;\n threshold: number;\n };\n}\n\nexport interface OptimizeResult {\n action: 'retry' | 'notify' | 'block';\n messages?: Message[];\n model?: string;\n}\n\n/**\n * Error thrown when guard mode is 'block' and a hard limit is exceeded.\n */\nexport class NeuraMeterGuardError extends Error {\n readonly rule: string;\n readonly current: number;\n readonly threshold: number;\n readonly suggestion: string;\n\n constructor(rule: TriggeredRule, suggestion: string) {\n super(`NeuraMeter guard: ${rule.ruleType} exceeded (${rule.currentValue} > ${rule.threshold})`);\n this.name = 'NeuraMeterGuardError';\n this.rule = rule.ruleType;\n this.current = rule.currentValue;\n this.threshold = rule.threshold;\n this.suggestion = suggestion;\n }\n}\n\n/**\n * Check guard rules before an API call.\n * Returns the decision and any triggered rules.\n */\nexport function checkGuards(\n config: GuardsConfig,\n params: {\n messages: Message[];\n model: string;\n provider: string;\n agentName: string;\n },\n hourlyCostDollars?: number,\n): GuardCheckResult {\n const mode = config.mode ?? 'notify';\n const triggeredRules: TriggeredRule[] = [];\n\n // Context analysis\n const contextAnalysis = analyzeContext(params.messages, params.model);\n\n // Check input tokens\n if (config.maxInputTokens && contextAnalysis.estimatedInputTokens > config.maxInputTokens) {\n triggeredRules.push({\n ruleType: 'input_tokens',\n currentValue: contextAnalysis.estimatedInputTokens,\n threshold: config.maxInputTokens,\n isHard: false,\n });\n }\n if (config.maxInputTokensHard && contextAnalysis.estimatedInputTokens > config.maxInputTokensHard) {\n triggeredRules.push({\n ruleType: 'input_tokens',\n currentValue: contextAnalysis.estimatedInputTokens,\n threshold: config.maxInputTokensHard,\n isHard: true,\n });\n }\n\n // Check context utilization\n if (config.maxContextUtilization && contextAnalysis.utilizationPercent > config.maxContextUtilization) {\n triggeredRules.push({\n ruleType: 'context_utilization',\n currentValue: contextAnalysis.utilizationPercent,\n threshold: config.maxContextUtilization,\n isHard: false,\n });\n }\n if (config.maxContextUtilizationHard && contextAnalysis.utilizationPercent > config.maxContextUtilizationHard) {\n triggeredRules.push({\n ruleType: 'context_utilization',\n currentValue: contextAnalysis.utilizationPercent,\n threshold: config.maxContextUtilizationHard,\n isHard: true,\n });\n }\n\n // Check estimated cost per call\n if (config.maxCostPerCall || config.maxCostPerCallHard) {\n const pricing = getModelPricing(params.provider, params.model);\n if (pricing) {\n const estimatedCost = calculateCostMicrodollars(\n { inputTokens: contextAnalysis.estimatedInputTokens, outputTokens: 0 },\n pricing,\n );\n const estimatedDollars = estimatedCost / 1_000_000;\n\n if (config.maxCostPerCall && estimatedDollars > config.maxCostPerCall) {\n triggeredRules.push({\n ruleType: 'cost_per_call',\n currentValue: estimatedDollars,\n threshold: config.maxCostPerCall,\n isHard: false,\n });\n }\n if (config.maxCostPerCallHard && estimatedDollars > config.maxCostPerCallHard) {\n triggeredRules.push({\n ruleType: 'cost_per_call',\n currentValue: estimatedDollars,\n threshold: config.maxCostPerCallHard,\n isHard: true,\n });\n }\n }\n }\n\n // Check cost per hour\n if (config.maxCostPerHour && hourlyCostDollars !== undefined && hourlyCostDollars > config.maxCostPerHour) {\n triggeredRules.push({\n ruleType: 'cost_per_hour',\n currentValue: hourlyCostDollars,\n threshold: config.maxCostPerHour,\n isHard: mode === 'block',\n });\n }\n\n // Generate suggestion\n const suggestion = generateSuggestion(triggeredRules, contextAnalysis);\n\n // Determine decision based on mode\n if (triggeredRules.length === 0) {\n return { decision: 'allow', triggeredRules, contextAnalysis, suggestion };\n }\n\n const hasHardViolation = triggeredRules.some((r) => r.isHard);\n\n switch (mode) {\n case 'notify':\n return { decision: 'notify', triggeredRules, contextAnalysis, suggestion };\n\n case 'block':\n if (hasHardViolation) {\n return { decision: 'block', triggeredRules, contextAnalysis, suggestion };\n }\n return { decision: 'notify', triggeredRules, contextAnalysis, suggestion };\n\n case 'auto-optimize':\n return { decision: 'notify', triggeredRules, contextAnalysis, suggestion };\n\n default:\n return { decision: 'allow', triggeredRules, contextAnalysis, suggestion };\n }\n}\n\nfunction generateSuggestion(rules: TriggeredRule[], ctx: ContextAnalysis): string {\n if (rules.length === 0) return '';\n\n const parts: string[] = [];\n\n for (const rule of rules) {\n switch (rule.ruleType) {\n case 'context_utilization':\n if (ctx.conversationTokens > ctx.systemPromptTokens) {\n const savingPct = Math.round((ctx.conversationTokens / ctx.estimatedInputTokens) * 100);\n parts.push(`Summarize conversation history to save ~${savingPct}% of input tokens`);\n }\n break;\n case 'input_tokens':\n parts.push(`Reduce input tokens from ${ctx.estimatedInputTokens.toLocaleString()} to under ${rule.threshold.toLocaleString()}`);\n break;\n case 'cost_per_call':\n parts.push('Consider using a cheaper model (e.g., gpt-4o-mini or claude-haiku)');\n break;\n case 'cost_per_hour':\n parts.push(`Hourly cost limit exceeded ($${rule.currentValue.toFixed(2)} > $${rule.threshold.toFixed(2)}). Throttle or pause agent calls`);\n break;\n }\n }\n\n return parts.join('. ') || 'Review guard thresholds or optimize context usage';\n}\n","import type { CostEvent, Provider, TraceOptions, TokenUsage } from './types';\nimport { calculateCostMicrodollars } from './cost';\nimport { getModelPricing } from './pricing';\n\nexport class Trace {\n readonly traceId: string;\n private readonly agentName: string;\n private readonly customerId?: string;\n private readonly taskName?: string;\n private readonly tags?: Record<string, string>;\n private readonly recordFn: (event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId' | 'cost'>) => void;\n\n constructor(\n opts: TraceOptions,\n recordFn: (event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId' | 'cost'>) => void,\n ) {\n this.traceId = crypto.randomUUID();\n this.agentName = opts.agentName;\n this.customerId = opts.customerId;\n this.taskName = opts.taskName;\n this.tags = opts.tags;\n this.recordFn = recordFn;\n }\n\n span(opts: {\n provider: Provider;\n model: string;\n usage: TokenUsage;\n latencyMs: number;\n parentSpanId?: string;\n agentName?: string;\n taskName?: string;\n tags?: Record<string, string>;\n }): string {\n const spanId = crypto.randomUUID();\n const pricing = getModelPricing(opts.provider, opts.model);\n const costMicrodollars = pricing\n ? calculateCostMicrodollars(opts.usage, pricing)\n : 0;\n\n this.recordFn({\n traceId: this.traceId,\n spanId,\n parentSpanId: opts.parentSpanId,\n agentName: opts.agentName ?? this.agentName,\n taskName: opts.taskName ?? this.taskName,\n customerId: this.customerId,\n provider: opts.provider,\n model: opts.model,\n inputTokens: opts.usage.inputTokens,\n outputTokens: opts.usage.outputTokens,\n reasoningTokens: opts.usage.reasoningTokens,\n cachedTokens: opts.usage.cachedTokens,\n costMicrodollars,\n latencyMs: opts.latencyMs,\n tags: { ...this.tags, ...opts.tags },\n });\n\n return spanId;\n }\n}\n","/**\n * Slack notification utility for NeuraMeter guard alerts.\n * Sends Block Kit formatted messages via incoming webhooks.\n */\n\nexport interface SlackMessage {\n /** Plain text fallback */\n text: string;\n /** Name of the agent that triggered the guard */\n agentName: string;\n /** Type of guard rule triggered (e.g. 'context_utilization', 'input_tokens') */\n ruleType: string;\n /** Current value that exceeded the threshold */\n currentValue: number;\n /** Configured threshold that was exceeded */\n threshold: number;\n /** Optimization suggestion from the guard system */\n suggestion?: string;\n}\n\n/**\n * Format a value for display based on the rule type.\n */\nfunction formatValue(ruleType: string, value: number): string {\n switch (ruleType) {\n case 'context_utilization':\n return `${value.toFixed(1)}%`;\n case 'cost_per_call':\n case 'cost_per_hour':\n case 'budget':\n return `$${value.toFixed(4)}`;\n case 'input_tokens':\n return value.toLocaleString();\n default:\n return String(value);\n }\n}\n\n/**\n * Format a rule type string for human-readable display.\n */\nfunction formatRuleType(ruleType: string): string {\n return ruleType\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\n/**\n * Build a Slack Block Kit payload for a guard alert.\n */\nfunction buildSlackPayload(message: SlackMessage): object {\n const blocks: object[] = [\n {\n type: 'header',\n text: {\n type: 'plain_text',\n text: `:warning: NeuraMeter Guard Alert`,\n emoji: true,\n },\n },\n {\n type: 'section',\n fields: [\n {\n type: 'mrkdwn',\n text: `*Agent:*\\n${message.agentName}`,\n },\n {\n type: 'mrkdwn',\n text: `*Rule Triggered:*\\n${formatRuleType(message.ruleType)}`,\n },\n {\n type: 'mrkdwn',\n text: `*Current Value:*\\n${formatValue(message.ruleType, message.currentValue)}`,\n },\n {\n type: 'mrkdwn',\n text: `*Threshold:*\\n${formatValue(message.ruleType, message.threshold)}`,\n },\n ],\n },\n ];\n\n if (message.suggestion) {\n blocks.push({\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `*Suggestion:*\\n${message.suggestion}`,\n },\n });\n }\n\n blocks.push({\n type: 'context',\n elements: [\n {\n type: 'mrkdwn',\n text: `Sent by NeuraMeter at ${new Date().toISOString()}`,\n },\n ],\n });\n\n return {\n text: message.text,\n blocks,\n };\n}\n\n/**\n * Send a Slack notification via an incoming webhook.\n * Fire-and-forget: errors are silently caught and do not propagate.\n *\n * @param webhookUrl - Slack incoming webhook URL\n * @param message - Structured alert message\n */\nexport async function sendSlackNotification(\n webhookUrl: string,\n message: SlackMessage,\n): Promise<void> {\n try {\n const payload = buildSlackPayload(message);\n await fetch(webhookUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n } catch {\n // Fire-and-forget: silently ignore errors\n }\n}\n","import type { CostEvent, NeuraMeterConfig, TraceOptions } from './types';\nimport type { GuardsConfig, GuardCheckResult, GuardEvent, OptimizeEvent, OptimizeResult } from './guards';\nimport { checkGuards, NeuraMeterGuardError } from './guards';\nimport type { Message } from './context';\nimport { Trace } from './trace';\nimport { calculateCostMicrodollars } from './cost';\nimport { getModelPricing } from './pricing';\nimport { sendSlackNotification } from './slack';\n\nconst DEFAULT_BATCH_SIZE = 50;\nconst DEFAULT_FLUSH_INTERVAL_MS = 5_000;\n\n/** Tracks cost accumulated within a rolling 1-hour window per agent. */\ninterface HourlyCostEntry {\n costMicrodollars: number;\n timestamp: number;\n}\n\nexport class NeuraMeter {\n private readonly apiKey: string;\n private readonly projectId: string;\n private readonly endpoint: string;\n private readonly batchSize: number;\n private readonly flushIntervalMs: number;\n readonly guards: GuardsConfig | undefined;\n\n private buffer: CostEvent[] = [];\n private guardBuffer: GuardEvent[] = [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private timer: any = null;\n private flushing = false;\n\n /** Rolling hourly cost tracking per agent for maxCostPerHour guard */\n private hourlyCosts: Map<string, HourlyCostEntry[]> = new Map();\n\n /** Extracted from API key format: nm_{orgId}_{secret} */\n private readonly orgId: string;\n\n constructor(config: NeuraMeterConfig) {\n this.apiKey = config.apiKey;\n this.projectId = config.projectId;\n this.endpoint = config.endpoint ?? 'https://neurameter-ingestion.neurameter.workers.dev';\n this.batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\n this.flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n this.guards = config.guards;\n\n // Extract orgId from API key (format: nm_{orgId}_{secret})\n const parts = this.apiKey.split('_');\n this.orgId = parts.length >= 3 ? parts[1]! : 'unknown';\n\n this.startAutoFlush();\n }\n\n startTrace(opts: TraceOptions): Trace {\n return new Trace(opts, (event) => this.record(event));\n }\n\n /**\n * Get rolling hourly cost for an agent (in dollars).\n */\n getHourlyCostDollars(agentName: string): number {\n const oneHourAgo = Date.now() - 3_600_000;\n const entries = this.hourlyCosts.get(agentName) ?? [];\n // Prune old entries\n const recent = entries.filter((e) => e.timestamp > oneHourAgo);\n this.hourlyCosts.set(agentName, recent);\n const totalMicro = recent.reduce((s, e) => s + e.costMicrodollars, 0);\n return totalMicro / 1_000_000;\n }\n\n /**\n * Track cost for hourly rate limiting.\n */\n private trackHourlyCost(agentName: string, costMicrodollars: number): void {\n const entries = this.hourlyCosts.get(agentName) ?? [];\n entries.push({ costMicrodollars, timestamp: Date.now() });\n this.hourlyCosts.set(agentName, entries);\n }\n\n /**\n * Check guard rules before an API call.\n * Returns the check result. In block mode, may throw NeuraMeterGuardError.\n */\n checkGuards(params: {\n messages: Message[];\n model: string;\n provider: string;\n agentName: string;\n }): GuardCheckResult | null {\n if (!this.guards) return null;\n\n const hourlyCostDollars = this.getHourlyCostDollars(params.agentName);\n const result = checkGuards(this.guards, params, hourlyCostDollars);\n\n // Record guard event (async, fire-and-forget)\n if (result.triggeredRules.length > 0) {\n this.recordGuardEvent({\n eventId: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n agentName: params.agentName,\n guardMode: this.guards.mode ?? 'notify',\n decision: result.decision,\n triggeredRules: result.triggeredRules,\n contextAnalysis: result.contextAnalysis ?? undefined,\n suggestion: result.suggestion,\n });\n\n // Send Slack notification if configured (async, fire-and-forget)\n if (this.guards.notifySlackWebhook) {\n for (const rule of result.triggeredRules) {\n void sendSlackNotification(this.guards.notifySlackWebhook, {\n text: `NeuraMeter guard triggered: ${rule.ruleType} for agent \"${params.agentName}\" (${rule.currentValue} > ${rule.threshold})`,\n agentName: params.agentName,\n ruleType: rule.ruleType,\n currentValue: rule.currentValue,\n threshold: rule.threshold,\n suggestion: result.suggestion,\n });\n }\n }\n }\n\n // In block mode with hard violation, throw\n if (result.decision === 'block') {\n const hardRule = result.triggeredRules.find((r) => r.isHard);\n if (hardRule) {\n throw new NeuraMeterGuardError(hardRule, result.suggestion ?? '');\n }\n }\n\n return result;\n }\n\n /**\n * Run auto-optimize flow: calls the onOptimize callback and returns the result.\n * Used by SDK wrappers when mode is 'auto-optimize' and thresholds are exceeded.\n */\n async runAutoOptimize(params: {\n guardResult: GuardCheckResult;\n messages: Message[];\n model: string;\n agentName: string;\n }): Promise<OptimizeResult | null> {\n if (!this.guards?.onOptimize || params.guardResult.triggeredRules.length === 0) {\n return null;\n }\n\n const primaryRule = params.guardResult.triggeredRules[0]!;\n const optimizeEvent: OptimizeEvent = {\n type: primaryRule.ruleType as OptimizeEvent['type'],\n suggestion: params.guardResult.suggestion ?? '',\n metrics: {\n messages: params.messages,\n model: params.model,\n currentValue: primaryRule.currentValue,\n threshold: primaryRule.threshold,\n },\n };\n\n try {\n const result = await this.guards.onOptimize(optimizeEvent);\n\n // Record optimization in guard event\n const pricing = getModelPricing(\n 'openai', // Best effort — wrappers will provide actual provider\n result.model ?? params.model,\n );\n const tokensBefore = params.guardResult.contextAnalysis?.estimatedInputTokens ?? 0;\n const costBefore = pricing\n ? calculateCostMicrodollars({ inputTokens: tokensBefore, outputTokens: 0 }, pricing) / 1_000_000\n : 0;\n\n this.recordGuardEvent({\n eventId: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n agentName: params.agentName,\n guardMode: 'auto-optimize',\n decision: result.action === 'retry' ? 'optimized' : result.action === 'block' ? 'block' : 'notify',\n triggeredRules: params.guardResult.triggeredRules,\n contextAnalysis: params.guardResult.contextAnalysis ?? undefined,\n optimization: {\n action: result.action,\n tokensBefore,\n costBefore,\n description: params.guardResult.suggestion,\n },\n suggestion: params.guardResult.suggestion,\n });\n\n return result;\n } catch {\n // If onOptimize fails, fall through to notify\n return { action: 'notify' };\n }\n }\n\n record(event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId' | 'cost'>): void {\n const fullEvent: CostEvent = {\n ...event,\n eventId: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n orgId: this.orgId,\n projectId: this.projectId,\n cost: event.costMicrodollars / 1_000_000,\n };\n\n this.buffer.push(fullEvent);\n\n // Track hourly cost for rate limiting\n if (event.agentName && event.costMicrodollars > 0) {\n this.trackHourlyCost(event.agentName, event.costMicrodollars);\n }\n\n if (this.buffer.length >= this.batchSize) {\n void this.flush();\n }\n }\n\n private recordGuardEvent(event: GuardEvent): void {\n this.guardBuffer.push(event);\n // Flush guard events alongside regular events\n }\n\n async flush(): Promise<void> {\n if ((this.buffer.length === 0 && this.guardBuffer.length === 0) || this.flushing) return;\n\n this.flushing = true;\n\n // Flush cost events\n if (this.buffer.length > 0) {\n const batch = this.buffer.splice(0, this.batchSize);\n try {\n const response = await fetch(`${this.endpoint}/v1/events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ batch }),\n });\n if (!response.ok) {\n this.buffer.unshift(...batch);\n }\n } catch {\n this.buffer.unshift(...batch);\n }\n }\n\n // Flush guard events\n if (this.guardBuffer.length > 0) {\n const guardBatch = this.guardBuffer.splice(0, this.batchSize);\n try {\n await fetch(`${this.endpoint}/v1/guard-events`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ batch: guardBatch }),\n });\n } catch {\n // Fire-and-forget for guard events\n }\n }\n\n this.flushing = false;\n }\n\n async destroy(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n\n private startAutoFlush(): void {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushIntervalMs);\n\n if (this.timer && typeof this.timer === 'object' && 'unref' in this.timer) {\n (this.timer as { unref: () => void }).unref();\n }\n }\n}\n"]}
|