@neurameter/core 0.1.0

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/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # @neurameter/core
2
+
3
+ Core SDK for NeuraMeter — real-time AI agent cost tracking.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @neurameter/core
9
+ # or
10
+ pnpm add @neurameter/core
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { NeuraMeter } from '@neurameter/core';
17
+
18
+ const meter = new NeuraMeter({
19
+ apiKey: 'nm_xxx',
20
+ projectId: 'proj_xxx',
21
+ });
22
+
23
+ // Record a cost event manually
24
+ meter.record({
25
+ traceId: 'trace-123',
26
+ spanId: 'span-456',
27
+ agentName: 'SupportAgent',
28
+ provider: 'openai',
29
+ model: 'gpt-4o',
30
+ inputTokens: 1500,
31
+ outputTokens: 800,
32
+ costMicrodollars: 11750,
33
+ latencyMs: 450,
34
+ });
35
+
36
+ // Flush all buffered events before shutdown
37
+ await meter.flush();
38
+ ```
39
+
40
+ ## Multi-Agent Tracing
41
+
42
+ ```typescript
43
+ const meter = new NeuraMeter({ apiKey: 'nm_xxx', projectId: 'proj_xxx' });
44
+
45
+ // Start a trace for a multi-agent workflow
46
+ const trace = meter.startTrace({
47
+ agentName: 'OrchestratorAgent',
48
+ customerId: 'cust_123',
49
+ });
50
+
51
+ // Record spans for each agent call
52
+ const parentSpan = trace.span({
53
+ provider: 'openai',
54
+ model: 'gpt-4o',
55
+ usage: { inputTokens: 500, outputTokens: 200 },
56
+ latencyMs: 300,
57
+ });
58
+
59
+ // Child spans
60
+ trace.span({
61
+ provider: 'anthropic',
62
+ model: 'claude-sonnet-4-20250514',
63
+ usage: { inputTokens: 1000, outputTokens: 500 },
64
+ latencyMs: 800,
65
+ parentSpanId: parentSpan,
66
+ agentName: 'DraftAgent',
67
+ });
68
+ ```
69
+
70
+ ## With OpenAI / Anthropic SDKs
71
+
72
+ Use `@neurameter/openai` or `@neurameter/anthropic` for automatic instrumentation:
73
+
74
+ ```typescript
75
+ import { NeuraMeter } from '@neurameter/core';
76
+ import { withMeter } from '@neurameter/openai';
77
+ import OpenAI from 'openai';
78
+
79
+ const meter = new NeuraMeter({ apiKey: 'nm_xxx', projectId: 'proj_xxx' });
80
+ const openai = withMeter(new OpenAI(), meter);
81
+
82
+ // Automatically tracks tokens, cost, and latency
83
+ const response = await openai.chat.completions.create(
84
+ { model: 'gpt-4o', messages: [{ role: 'user', content: 'Hello' }] },
85
+ { agentName: 'SupportAgent', taskName: 'classify-ticket' },
86
+ );
87
+ ```
88
+
89
+ ## CostEvent Type
90
+
91
+ ```typescript
92
+ interface CostEvent {
93
+ eventId: string; // Auto-generated UUID
94
+ timestamp: string; // ISO 8601
95
+ traceId: string; // Groups related spans
96
+ spanId: string; // Unique span identifier
97
+ parentSpanId?: string; // Parent span (for tree structure)
98
+ agentName: string; // Name of the AI agent
99
+ taskName?: string; // Optional task label
100
+ customerId?: string; // End-user identifier
101
+ provider: 'openai' | 'anthropic' | 'google' | 'other';
102
+ model: string; // Model identifier
103
+ inputTokens: number; // Prompt tokens
104
+ outputTokens: number; // Completion tokens
105
+ reasoningTokens?: number; // Reasoning tokens (o1, o3)
106
+ cachedTokens?: number; // Cached input tokens
107
+ costMicrodollars: number; // Cost in microdollars (1 USD = 1,000,000)
108
+ latencyMs: number; // Response latency
109
+ tags?: Record<string, string>;
110
+ orgId: string; // Organization ID
111
+ projectId: string; // Project ID
112
+ }
113
+ ```
114
+
115
+ ## Key Design Decisions
116
+
117
+ - **Zero dependencies** — only uses native `fetch()` and `crypto`
118
+ - **Integer cost arithmetic** — all costs in microdollars (no floating-point)
119
+ - **Fire-and-forget** — SDK errors never propagate to your application
120
+ - **Batch sending** — events are buffered and sent in batches of 50-100
121
+ - **< 5KB gzipped** — minimal impact on bundle size
package/dist/index.cjs ADDED
@@ -0,0 +1,8 @@
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){return M[t]?.[e]}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
+ ${t.agentName}`},{type:"mrkdwn",text:`*Rule Triggered:*
3
+ ${b(t.ruleType)}`},{type:"mrkdwn",text:`*Current Value:*
4
+ ${P(t.ruleType,t.currentValue)}`},{type:"mrkdwn",text:`*Threshold:*
5
+ ${P(t.ruleType,t.threshold)}`}]}];return t.suggestion&&e.push({type:"section",text:{type:"mrkdwn",text:`*Suggestion:*
6
+ ${t.suggestion}`}}),e.push({type:"context",elements:[{type:"mrkdwn",text:`Sent by NeuraMeter at ${new Date().toISOString()}`}]}),{text:t.text,blocks:e}}async function y(t,e){try{let r=v(e);await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)});}catch{}}var I=50,z=5e3,T=class{apiKey;projectId;endpoint;batchSize;flushIntervalMs;guards;buffer=[];guardBuffer=[];timer=null;flushing=false;hourlyCosts=new Map;orgId;constructor(e){this.apiKey=e.apiKey,this.projectId=e.projectId,this.endpoint=e.endpoint??"https://ingest.meter.neuria.tech",this.batchSize=e.batchSize??I,this.flushIntervalMs=e.flushIntervalMs??z,this.guards=e.guards;let r=this.apiKey.split("_");this.orgId=r.length>=3?r[1]:"unknown",this.startAutoFlush();}startTrace(e){return new p(e,r=>this.record(r))}getHourlyCostDollars(e){let r=Date.now()-36e5,n=(this.hourlyCosts.get(e)??[]).filter(s=>s.timestamp>r);return this.hourlyCosts.set(e,n),n.reduce((s,a)=>s+a.costMicrodollars,0)/1e6}trackHourlyCost(e,r){let o=this.hourlyCosts.get(e)??[];o.push({costMicrodollars:r,timestamp:Date.now()}),this.hourlyCosts.set(e,o);}checkGuards(e){if(!this.guards)return null;let r=this.getHourlyCostDollars(e.agentName),o=k(this.guards,e,r);if(o.triggeredRules.length>0&&(this.recordGuardEvent({eventId:crypto.randomUUID(),timestamp:new Date().toISOString(),agentName:e.agentName,guardMode:this.guards.mode??"notify",decision:o.decision,triggeredRules:o.triggeredRules,contextAnalysis:o.contextAnalysis??void 0,suggestion:o.suggestion}),this.guards.notifySlackWebhook))for(let n of o.triggeredRules)y(this.guards.notifySlackWebhook,{text:`NeuraMeter guard triggered: ${n.ruleType} for agent "${e.agentName}" (${n.currentValue} > ${n.threshold})`,agentName:e.agentName,ruleType:n.ruleType,currentValue:n.currentValue,threshold:n.threshold,suggestion:o.suggestion});if(o.decision==="block"){let n=o.triggeredRules.find(i=>i.isHard);if(n)throw new d(n,o.suggestion??"")}return o}async runAutoOptimize(e){if(!this.guards?.onOptimize||e.guardResult.triggeredRules.length===0)return null;let r=e.guardResult.triggeredRules[0],o={type:r.ruleType,suggestion:e.guardResult.suggestion??"",metrics:{messages:e.messages,model:e.model,currentValue:r.currentValue,threshold:r.threshold}};try{let n=await this.guards.onOptimize(o),i=l("openai",n.model??e.model),s=e.guardResult.contextAnalysis?.estimatedInputTokens??0,a=i?c({inputTokens:s,outputTokens:0},i)/1e6:0;return this.recordGuardEvent({eventId:crypto.randomUUID(),timestamp:new Date().toISOString(),agentName:e.agentName,guardMode:"auto-optimize",decision:n.action==="retry"?"optimized":n.action==="block"?"block":"notify",triggeredRules:e.guardResult.triggeredRules,contextAnalysis:e.guardResult.contextAnalysis??void 0,optimization:{action:n.action,tokensBefore:s,costBefore:a,description:e.guardResult.suggestion},suggestion:e.guardResult.suggestion}),n}catch{return {action:"notify"}}}record(e){let r={...e,eventId:crypto.randomUUID(),timestamp:new Date().toISOString(),orgId:this.orgId,projectId:this.projectId};this.buffer.push(r),e.agentName&&e.costMicrodollars>0&&this.trackHourlyCost(e.agentName,e.costMicrodollars),this.buffer.length>=this.batchSize&&this.flush();}recordGuardEvent(e){this.guardBuffer.push(e);}async flush(){if(!(this.buffer.length===0&&this.guardBuffer.length===0||this.flushing)){if(this.flushing=true,this.buffer.length>0){let e=this.buffer.splice(0,this.batchSize);try{(await fetch(`${this.endpoint}/v1/events`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:JSON.stringify({batch:e})})).ok||this.buffer.unshift(...e);}catch{this.buffer.unshift(...e);}}if(this.guardBuffer.length>0){let e=this.guardBuffer.splice(0,this.batchSize);try{await fetch(`${this.endpoint}/v1/guard-events`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:JSON.stringify({batch:e})});}catch{}}this.flushing=false;}}destroy(){this.timer&&(clearInterval(this.timer),this.timer=null),this.flush();}startAutoFlush(){this.timer=setInterval(()=>{this.flush();},this.flushIntervalMs),this.timer&&typeof this.timer=="object"&&"unref"in this.timer&&this.timer.unref();}};
7
+ exports.MODEL_CONTEXT_LIMITS=m;exports.NeuraMeter=T;exports.NeuraMeterGuardError=d;exports.Trace=p;exports.analyzeContext=f;exports.calculateCostMicrodollars=c;exports.checkGuards=k;exports.estimateTokens=_;exports.getModelContextLimit=x;exports.getModelPricing=l;exports.sendSlackNotification=y;//# sourceMappingURL=index.cjs.map
8
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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'>) => void;\n\n constructor(\n opts: TraceOptions,\n recordFn: (event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId'>) => 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://ingest.meter.neuria.tech';\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'>): 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 };\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 destroy(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n void 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"]}
@@ -0,0 +1,293 @@
1
+ interface ContextAnalysis {
2
+ estimatedInputTokens: number;
3
+ modelContextLimit: number;
4
+ utilizationPercent: number;
5
+ messageCount: number;
6
+ systemPromptTokens: number;
7
+ conversationTokens: number;
8
+ toolResultTokens: number;
9
+ }
10
+ interface Message {
11
+ role: string;
12
+ content: string | unknown;
13
+ }
14
+ /**
15
+ * Model context window limits (tokens).
16
+ */
17
+ declare const MODEL_CONTEXT_LIMITS: Record<string, number>;
18
+ /**
19
+ * Estimate token count from a string.
20
+ * Uses a simple heuristic: ~4 chars per token for English,
21
+ * ~1.5 chars per token for CJK/mixed content.
22
+ * This is intentionally fast (<1ms) for SDK use.
23
+ */
24
+ declare function estimateTokens(content: string | unknown): number;
25
+ /**
26
+ * Analyze context window utilization for a set of messages.
27
+ */
28
+ declare function analyzeContext(messages: Message[], model: string): ContextAnalysis;
29
+ /**
30
+ * Get model context limit with prefix matching fallback.
31
+ */
32
+ declare function getModelContextLimit(model: string): number;
33
+
34
+ type GuardMode = 'notify' | 'block' | 'auto-optimize';
35
+ type GuardDecision = 'allow' | 'notify' | 'block' | 'optimized';
36
+ interface GuardsConfig {
37
+ maxInputTokens?: number;
38
+ maxInputTokensHard?: number;
39
+ maxContextUtilization?: number;
40
+ maxContextUtilizationHard?: number;
41
+ maxCostPerCall?: number;
42
+ maxCostPerCallHard?: number;
43
+ maxCostPerHour?: number;
44
+ mode?: GuardMode;
45
+ notifySlackWebhook?: string;
46
+ notifyDashboard?: boolean;
47
+ onOptimize?: (event: OptimizeEvent) => Promise<OptimizeResult>;
48
+ }
49
+ interface TriggeredRule {
50
+ ruleType: 'input_tokens' | 'cost_per_call' | 'cost_per_hour' | 'context_utilization' | 'budget';
51
+ currentValue: number;
52
+ threshold: number;
53
+ isHard: boolean;
54
+ }
55
+ interface GuardCheckResult {
56
+ decision: GuardDecision;
57
+ triggeredRules: TriggeredRule[];
58
+ contextAnalysis: ContextAnalysis | null;
59
+ suggestion?: string;
60
+ }
61
+ interface GuardEvent {
62
+ eventId: string;
63
+ timestamp: string;
64
+ agentName: string;
65
+ guardMode: GuardMode;
66
+ decision: GuardDecision;
67
+ triggeredRules: TriggeredRule[];
68
+ contextAnalysis?: ContextAnalysis;
69
+ optimization?: {
70
+ action: 'retry' | 'notify' | 'block';
71
+ tokensBefore: number;
72
+ tokensAfter?: number;
73
+ costBefore: number;
74
+ costAfter?: number;
75
+ description?: string;
76
+ };
77
+ suggestion?: string;
78
+ }
79
+ interface OptimizeEvent {
80
+ type: 'context_utilization' | 'cost_per_call' | 'input_tokens';
81
+ suggestion: string;
82
+ metrics: {
83
+ messages?: Message[];
84
+ model?: string;
85
+ currentValue: number;
86
+ threshold: number;
87
+ };
88
+ }
89
+ interface OptimizeResult {
90
+ action: 'retry' | 'notify' | 'block';
91
+ messages?: Message[];
92
+ model?: string;
93
+ }
94
+ /**
95
+ * Error thrown when guard mode is 'block' and a hard limit is exceeded.
96
+ */
97
+ declare class NeuraMeterGuardError extends Error {
98
+ readonly rule: string;
99
+ readonly current: number;
100
+ readonly threshold: number;
101
+ readonly suggestion: string;
102
+ constructor(rule: TriggeredRule, suggestion: string);
103
+ }
104
+ /**
105
+ * Check guard rules before an API call.
106
+ * Returns the decision and any triggered rules.
107
+ */
108
+ declare function checkGuards(config: GuardsConfig, params: {
109
+ messages: Message[];
110
+ model: string;
111
+ provider: string;
112
+ agentName: string;
113
+ }, hourlyCostDollars?: number): GuardCheckResult;
114
+
115
+ type Provider = 'openai' | 'anthropic' | 'google' | 'other';
116
+ interface CostEvent {
117
+ eventId: string;
118
+ timestamp: string;
119
+ traceId: string;
120
+ spanId: string;
121
+ parentSpanId?: string;
122
+ agentName: string;
123
+ taskName?: string;
124
+ customerId?: string;
125
+ provider: Provider;
126
+ model: string;
127
+ inputTokens: number;
128
+ outputTokens: number;
129
+ reasoningTokens?: number;
130
+ cachedTokens?: number;
131
+ costMicrodollars: number;
132
+ latencyMs: number;
133
+ contextUtilization?: number;
134
+ modelContextLimit?: number;
135
+ messageCount?: number;
136
+ systemPromptTokens?: number;
137
+ conversationTokens?: number;
138
+ toolResultTokens?: number;
139
+ guardMode?: GuardMode;
140
+ guardDecision?: GuardDecision;
141
+ guardTriggeredRules?: string[];
142
+ tags?: Record<string, string>;
143
+ orgId: string;
144
+ projectId: string;
145
+ }
146
+ interface NeuraMeterConfig {
147
+ apiKey: string;
148
+ projectId: string;
149
+ endpoint?: string;
150
+ /** Max events per batch (default: 50) */
151
+ batchSize?: number;
152
+ /** Flush interval in ms (default: 5000) */
153
+ flushIntervalMs?: number;
154
+ /** v2.0 Guard rails configuration */
155
+ guards?: GuardsConfig;
156
+ }
157
+ interface TraceOptions {
158
+ agentName: string;
159
+ customerId?: string;
160
+ taskName?: string;
161
+ tags?: Record<string, string>;
162
+ }
163
+ interface ModelPricing {
164
+ inputPricePerMToken: number;
165
+ outputPricePerMToken: number;
166
+ reasoningPricePerMToken?: number;
167
+ cachedInputPricePerMToken?: number;
168
+ }
169
+ interface TokenUsage {
170
+ inputTokens: number;
171
+ outputTokens: number;
172
+ reasoningTokens?: number;
173
+ cachedTokens?: number;
174
+ }
175
+
176
+ declare class Trace {
177
+ readonly traceId: string;
178
+ private readonly agentName;
179
+ private readonly customerId?;
180
+ private readonly taskName?;
181
+ private readonly tags?;
182
+ private readonly recordFn;
183
+ constructor(opts: TraceOptions, recordFn: (event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId'>) => void);
184
+ span(opts: {
185
+ provider: Provider;
186
+ model: string;
187
+ usage: TokenUsage;
188
+ latencyMs: number;
189
+ parentSpanId?: string;
190
+ agentName?: string;
191
+ taskName?: string;
192
+ tags?: Record<string, string>;
193
+ }): string;
194
+ }
195
+
196
+ declare class NeuraMeter {
197
+ private readonly apiKey;
198
+ private readonly projectId;
199
+ private readonly endpoint;
200
+ private readonly batchSize;
201
+ private readonly flushIntervalMs;
202
+ readonly guards: GuardsConfig | undefined;
203
+ private buffer;
204
+ private guardBuffer;
205
+ private timer;
206
+ private flushing;
207
+ /** Rolling hourly cost tracking per agent for maxCostPerHour guard */
208
+ private hourlyCosts;
209
+ /** Extracted from API key format: nm_{orgId}_{secret} */
210
+ private readonly orgId;
211
+ constructor(config: NeuraMeterConfig);
212
+ startTrace(opts: TraceOptions): Trace;
213
+ /**
214
+ * Get rolling hourly cost for an agent (in dollars).
215
+ */
216
+ getHourlyCostDollars(agentName: string): number;
217
+ /**
218
+ * Track cost for hourly rate limiting.
219
+ */
220
+ private trackHourlyCost;
221
+ /**
222
+ * Check guard rules before an API call.
223
+ * Returns the check result. In block mode, may throw NeuraMeterGuardError.
224
+ */
225
+ checkGuards(params: {
226
+ messages: Message[];
227
+ model: string;
228
+ provider: string;
229
+ agentName: string;
230
+ }): GuardCheckResult | null;
231
+ /**
232
+ * Run auto-optimize flow: calls the onOptimize callback and returns the result.
233
+ * Used by SDK wrappers when mode is 'auto-optimize' and thresholds are exceeded.
234
+ */
235
+ runAutoOptimize(params: {
236
+ guardResult: GuardCheckResult;
237
+ messages: Message[];
238
+ model: string;
239
+ agentName: string;
240
+ }): Promise<OptimizeResult | null>;
241
+ record(event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId'>): void;
242
+ private recordGuardEvent;
243
+ flush(): Promise<void>;
244
+ destroy(): void;
245
+ private startAutoFlush;
246
+ }
247
+
248
+ /**
249
+ * Calculate cost in microdollars using integer arithmetic only.
250
+ *
251
+ * Pricing values are stored as microdollars per million tokens.
252
+ * Formula: tokens * pricePerMToken / 1_000_000
253
+ * (result is already in microdollars since price is in microdollars)
254
+ *
255
+ * To avoid floating-point, we compute:
256
+ * cost = Math.round(tokens * pricePerMToken / 1_000_000)
257
+ *
258
+ * Since pricePerMToken is already an integer (microdollars per 1M tokens),
259
+ * and tokens is an integer, the only division is by 1_000_000.
260
+ * We use Math.round to get the nearest integer microdollar.
261
+ */
262
+ declare function calculateCostMicrodollars(usage: TokenUsage, pricing: ModelPricing): number;
263
+
264
+ declare function getModelPricing(provider: string, model: string): ModelPricing | undefined;
265
+
266
+ /**
267
+ * Slack notification utility for NeuraMeter guard alerts.
268
+ * Sends Block Kit formatted messages via incoming webhooks.
269
+ */
270
+ interface SlackMessage {
271
+ /** Plain text fallback */
272
+ text: string;
273
+ /** Name of the agent that triggered the guard */
274
+ agentName: string;
275
+ /** Type of guard rule triggered (e.g. 'context_utilization', 'input_tokens') */
276
+ ruleType: string;
277
+ /** Current value that exceeded the threshold */
278
+ currentValue: number;
279
+ /** Configured threshold that was exceeded */
280
+ threshold: number;
281
+ /** Optimization suggestion from the guard system */
282
+ suggestion?: string;
283
+ }
284
+ /**
285
+ * Send a Slack notification via an incoming webhook.
286
+ * Fire-and-forget: errors are silently caught and do not propagate.
287
+ *
288
+ * @param webhookUrl - Slack incoming webhook URL
289
+ * @param message - Structured alert message
290
+ */
291
+ declare function sendSlackNotification(webhookUrl: string, message: SlackMessage): Promise<void>;
292
+
293
+ export { type ContextAnalysis, type CostEvent, type GuardCheckResult, type GuardDecision, type GuardEvent, type GuardMode, type GuardsConfig, MODEL_CONTEXT_LIMITS, type Message, type ModelPricing, NeuraMeter, type NeuraMeterConfig, NeuraMeterGuardError, type OptimizeEvent, type OptimizeResult, type Provider, type SlackMessage, type TokenUsage, Trace, type TraceOptions, type TriggeredRule, analyzeContext, calculateCostMicrodollars, checkGuards, estimateTokens, getModelContextLimit, getModelPricing, sendSlackNotification };
@@ -0,0 +1,293 @@
1
+ interface ContextAnalysis {
2
+ estimatedInputTokens: number;
3
+ modelContextLimit: number;
4
+ utilizationPercent: number;
5
+ messageCount: number;
6
+ systemPromptTokens: number;
7
+ conversationTokens: number;
8
+ toolResultTokens: number;
9
+ }
10
+ interface Message {
11
+ role: string;
12
+ content: string | unknown;
13
+ }
14
+ /**
15
+ * Model context window limits (tokens).
16
+ */
17
+ declare const MODEL_CONTEXT_LIMITS: Record<string, number>;
18
+ /**
19
+ * Estimate token count from a string.
20
+ * Uses a simple heuristic: ~4 chars per token for English,
21
+ * ~1.5 chars per token for CJK/mixed content.
22
+ * This is intentionally fast (<1ms) for SDK use.
23
+ */
24
+ declare function estimateTokens(content: string | unknown): number;
25
+ /**
26
+ * Analyze context window utilization for a set of messages.
27
+ */
28
+ declare function analyzeContext(messages: Message[], model: string): ContextAnalysis;
29
+ /**
30
+ * Get model context limit with prefix matching fallback.
31
+ */
32
+ declare function getModelContextLimit(model: string): number;
33
+
34
+ type GuardMode = 'notify' | 'block' | 'auto-optimize';
35
+ type GuardDecision = 'allow' | 'notify' | 'block' | 'optimized';
36
+ interface GuardsConfig {
37
+ maxInputTokens?: number;
38
+ maxInputTokensHard?: number;
39
+ maxContextUtilization?: number;
40
+ maxContextUtilizationHard?: number;
41
+ maxCostPerCall?: number;
42
+ maxCostPerCallHard?: number;
43
+ maxCostPerHour?: number;
44
+ mode?: GuardMode;
45
+ notifySlackWebhook?: string;
46
+ notifyDashboard?: boolean;
47
+ onOptimize?: (event: OptimizeEvent) => Promise<OptimizeResult>;
48
+ }
49
+ interface TriggeredRule {
50
+ ruleType: 'input_tokens' | 'cost_per_call' | 'cost_per_hour' | 'context_utilization' | 'budget';
51
+ currentValue: number;
52
+ threshold: number;
53
+ isHard: boolean;
54
+ }
55
+ interface GuardCheckResult {
56
+ decision: GuardDecision;
57
+ triggeredRules: TriggeredRule[];
58
+ contextAnalysis: ContextAnalysis | null;
59
+ suggestion?: string;
60
+ }
61
+ interface GuardEvent {
62
+ eventId: string;
63
+ timestamp: string;
64
+ agentName: string;
65
+ guardMode: GuardMode;
66
+ decision: GuardDecision;
67
+ triggeredRules: TriggeredRule[];
68
+ contextAnalysis?: ContextAnalysis;
69
+ optimization?: {
70
+ action: 'retry' | 'notify' | 'block';
71
+ tokensBefore: number;
72
+ tokensAfter?: number;
73
+ costBefore: number;
74
+ costAfter?: number;
75
+ description?: string;
76
+ };
77
+ suggestion?: string;
78
+ }
79
+ interface OptimizeEvent {
80
+ type: 'context_utilization' | 'cost_per_call' | 'input_tokens';
81
+ suggestion: string;
82
+ metrics: {
83
+ messages?: Message[];
84
+ model?: string;
85
+ currentValue: number;
86
+ threshold: number;
87
+ };
88
+ }
89
+ interface OptimizeResult {
90
+ action: 'retry' | 'notify' | 'block';
91
+ messages?: Message[];
92
+ model?: string;
93
+ }
94
+ /**
95
+ * Error thrown when guard mode is 'block' and a hard limit is exceeded.
96
+ */
97
+ declare class NeuraMeterGuardError extends Error {
98
+ readonly rule: string;
99
+ readonly current: number;
100
+ readonly threshold: number;
101
+ readonly suggestion: string;
102
+ constructor(rule: TriggeredRule, suggestion: string);
103
+ }
104
+ /**
105
+ * Check guard rules before an API call.
106
+ * Returns the decision and any triggered rules.
107
+ */
108
+ declare function checkGuards(config: GuardsConfig, params: {
109
+ messages: Message[];
110
+ model: string;
111
+ provider: string;
112
+ agentName: string;
113
+ }, hourlyCostDollars?: number): GuardCheckResult;
114
+
115
+ type Provider = 'openai' | 'anthropic' | 'google' | 'other';
116
+ interface CostEvent {
117
+ eventId: string;
118
+ timestamp: string;
119
+ traceId: string;
120
+ spanId: string;
121
+ parentSpanId?: string;
122
+ agentName: string;
123
+ taskName?: string;
124
+ customerId?: string;
125
+ provider: Provider;
126
+ model: string;
127
+ inputTokens: number;
128
+ outputTokens: number;
129
+ reasoningTokens?: number;
130
+ cachedTokens?: number;
131
+ costMicrodollars: number;
132
+ latencyMs: number;
133
+ contextUtilization?: number;
134
+ modelContextLimit?: number;
135
+ messageCount?: number;
136
+ systemPromptTokens?: number;
137
+ conversationTokens?: number;
138
+ toolResultTokens?: number;
139
+ guardMode?: GuardMode;
140
+ guardDecision?: GuardDecision;
141
+ guardTriggeredRules?: string[];
142
+ tags?: Record<string, string>;
143
+ orgId: string;
144
+ projectId: string;
145
+ }
146
+ interface NeuraMeterConfig {
147
+ apiKey: string;
148
+ projectId: string;
149
+ endpoint?: string;
150
+ /** Max events per batch (default: 50) */
151
+ batchSize?: number;
152
+ /** Flush interval in ms (default: 5000) */
153
+ flushIntervalMs?: number;
154
+ /** v2.0 Guard rails configuration */
155
+ guards?: GuardsConfig;
156
+ }
157
+ interface TraceOptions {
158
+ agentName: string;
159
+ customerId?: string;
160
+ taskName?: string;
161
+ tags?: Record<string, string>;
162
+ }
163
+ interface ModelPricing {
164
+ inputPricePerMToken: number;
165
+ outputPricePerMToken: number;
166
+ reasoningPricePerMToken?: number;
167
+ cachedInputPricePerMToken?: number;
168
+ }
169
+ interface TokenUsage {
170
+ inputTokens: number;
171
+ outputTokens: number;
172
+ reasoningTokens?: number;
173
+ cachedTokens?: number;
174
+ }
175
+
176
+ declare class Trace {
177
+ readonly traceId: string;
178
+ private readonly agentName;
179
+ private readonly customerId?;
180
+ private readonly taskName?;
181
+ private readonly tags?;
182
+ private readonly recordFn;
183
+ constructor(opts: TraceOptions, recordFn: (event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId'>) => void);
184
+ span(opts: {
185
+ provider: Provider;
186
+ model: string;
187
+ usage: TokenUsage;
188
+ latencyMs: number;
189
+ parentSpanId?: string;
190
+ agentName?: string;
191
+ taskName?: string;
192
+ tags?: Record<string, string>;
193
+ }): string;
194
+ }
195
+
196
+ declare class NeuraMeter {
197
+ private readonly apiKey;
198
+ private readonly projectId;
199
+ private readonly endpoint;
200
+ private readonly batchSize;
201
+ private readonly flushIntervalMs;
202
+ readonly guards: GuardsConfig | undefined;
203
+ private buffer;
204
+ private guardBuffer;
205
+ private timer;
206
+ private flushing;
207
+ /** Rolling hourly cost tracking per agent for maxCostPerHour guard */
208
+ private hourlyCosts;
209
+ /** Extracted from API key format: nm_{orgId}_{secret} */
210
+ private readonly orgId;
211
+ constructor(config: NeuraMeterConfig);
212
+ startTrace(opts: TraceOptions): Trace;
213
+ /**
214
+ * Get rolling hourly cost for an agent (in dollars).
215
+ */
216
+ getHourlyCostDollars(agentName: string): number;
217
+ /**
218
+ * Track cost for hourly rate limiting.
219
+ */
220
+ private trackHourlyCost;
221
+ /**
222
+ * Check guard rules before an API call.
223
+ * Returns the check result. In block mode, may throw NeuraMeterGuardError.
224
+ */
225
+ checkGuards(params: {
226
+ messages: Message[];
227
+ model: string;
228
+ provider: string;
229
+ agentName: string;
230
+ }): GuardCheckResult | null;
231
+ /**
232
+ * Run auto-optimize flow: calls the onOptimize callback and returns the result.
233
+ * Used by SDK wrappers when mode is 'auto-optimize' and thresholds are exceeded.
234
+ */
235
+ runAutoOptimize(params: {
236
+ guardResult: GuardCheckResult;
237
+ messages: Message[];
238
+ model: string;
239
+ agentName: string;
240
+ }): Promise<OptimizeResult | null>;
241
+ record(event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId'>): void;
242
+ private recordGuardEvent;
243
+ flush(): Promise<void>;
244
+ destroy(): void;
245
+ private startAutoFlush;
246
+ }
247
+
248
+ /**
249
+ * Calculate cost in microdollars using integer arithmetic only.
250
+ *
251
+ * Pricing values are stored as microdollars per million tokens.
252
+ * Formula: tokens * pricePerMToken / 1_000_000
253
+ * (result is already in microdollars since price is in microdollars)
254
+ *
255
+ * To avoid floating-point, we compute:
256
+ * cost = Math.round(tokens * pricePerMToken / 1_000_000)
257
+ *
258
+ * Since pricePerMToken is already an integer (microdollars per 1M tokens),
259
+ * and tokens is an integer, the only division is by 1_000_000.
260
+ * We use Math.round to get the nearest integer microdollar.
261
+ */
262
+ declare function calculateCostMicrodollars(usage: TokenUsage, pricing: ModelPricing): number;
263
+
264
+ declare function getModelPricing(provider: string, model: string): ModelPricing | undefined;
265
+
266
+ /**
267
+ * Slack notification utility for NeuraMeter guard alerts.
268
+ * Sends Block Kit formatted messages via incoming webhooks.
269
+ */
270
+ interface SlackMessage {
271
+ /** Plain text fallback */
272
+ text: string;
273
+ /** Name of the agent that triggered the guard */
274
+ agentName: string;
275
+ /** Type of guard rule triggered (e.g. 'context_utilization', 'input_tokens') */
276
+ ruleType: string;
277
+ /** Current value that exceeded the threshold */
278
+ currentValue: number;
279
+ /** Configured threshold that was exceeded */
280
+ threshold: number;
281
+ /** Optimization suggestion from the guard system */
282
+ suggestion?: string;
283
+ }
284
+ /**
285
+ * Send a Slack notification via an incoming webhook.
286
+ * Fire-and-forget: errors are silently caught and do not propagate.
287
+ *
288
+ * @param webhookUrl - Slack incoming webhook URL
289
+ * @param message - Structured alert message
290
+ */
291
+ declare function sendSlackNotification(webhookUrl: string, message: SlackMessage): Promise<void>;
292
+
293
+ export { type ContextAnalysis, type CostEvent, type GuardCheckResult, type GuardDecision, type GuardEvent, type GuardMode, type GuardsConfig, MODEL_CONTEXT_LIMITS, type Message, type ModelPricing, NeuraMeter, type NeuraMeterConfig, NeuraMeterGuardError, type OptimizeEvent, type OptimizeResult, type Provider, type SlackMessage, type TokenUsage, Trace, type TraceOptions, type TriggeredRule, analyzeContext, calculateCostMicrodollars, checkGuards, estimateTokens, getModelContextLimit, getModelPricing, sendSlackNotification };
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
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){return M[t]?.[e]}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
+ ${t.agentName}`},{type:"mrkdwn",text:`*Rule Triggered:*
3
+ ${b(t.ruleType)}`},{type:"mrkdwn",text:`*Current Value:*
4
+ ${P(t.ruleType,t.currentValue)}`},{type:"mrkdwn",text:`*Threshold:*
5
+ ${P(t.ruleType,t.threshold)}`}]}];return t.suggestion&&e.push({type:"section",text:{type:"mrkdwn",text:`*Suggestion:*
6
+ ${t.suggestion}`}}),e.push({type:"context",elements:[{type:"mrkdwn",text:`Sent by NeuraMeter at ${new Date().toISOString()}`}]}),{text:t.text,blocks:e}}async function y(t,e){try{let r=v(e);await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)});}catch{}}var I=50,z=5e3,T=class{apiKey;projectId;endpoint;batchSize;flushIntervalMs;guards;buffer=[];guardBuffer=[];timer=null;flushing=false;hourlyCosts=new Map;orgId;constructor(e){this.apiKey=e.apiKey,this.projectId=e.projectId,this.endpoint=e.endpoint??"https://ingest.meter.neuria.tech",this.batchSize=e.batchSize??I,this.flushIntervalMs=e.flushIntervalMs??z,this.guards=e.guards;let r=this.apiKey.split("_");this.orgId=r.length>=3?r[1]:"unknown",this.startAutoFlush();}startTrace(e){return new p(e,r=>this.record(r))}getHourlyCostDollars(e){let r=Date.now()-36e5,n=(this.hourlyCosts.get(e)??[]).filter(s=>s.timestamp>r);return this.hourlyCosts.set(e,n),n.reduce((s,a)=>s+a.costMicrodollars,0)/1e6}trackHourlyCost(e,r){let o=this.hourlyCosts.get(e)??[];o.push({costMicrodollars:r,timestamp:Date.now()}),this.hourlyCosts.set(e,o);}checkGuards(e){if(!this.guards)return null;let r=this.getHourlyCostDollars(e.agentName),o=k(this.guards,e,r);if(o.triggeredRules.length>0&&(this.recordGuardEvent({eventId:crypto.randomUUID(),timestamp:new Date().toISOString(),agentName:e.agentName,guardMode:this.guards.mode??"notify",decision:o.decision,triggeredRules:o.triggeredRules,contextAnalysis:o.contextAnalysis??void 0,suggestion:o.suggestion}),this.guards.notifySlackWebhook))for(let n of o.triggeredRules)y(this.guards.notifySlackWebhook,{text:`NeuraMeter guard triggered: ${n.ruleType} for agent "${e.agentName}" (${n.currentValue} > ${n.threshold})`,agentName:e.agentName,ruleType:n.ruleType,currentValue:n.currentValue,threshold:n.threshold,suggestion:o.suggestion});if(o.decision==="block"){let n=o.triggeredRules.find(i=>i.isHard);if(n)throw new d(n,o.suggestion??"")}return o}async runAutoOptimize(e){if(!this.guards?.onOptimize||e.guardResult.triggeredRules.length===0)return null;let r=e.guardResult.triggeredRules[0],o={type:r.ruleType,suggestion:e.guardResult.suggestion??"",metrics:{messages:e.messages,model:e.model,currentValue:r.currentValue,threshold:r.threshold}};try{let n=await this.guards.onOptimize(o),i=l("openai",n.model??e.model),s=e.guardResult.contextAnalysis?.estimatedInputTokens??0,a=i?c({inputTokens:s,outputTokens:0},i)/1e6:0;return this.recordGuardEvent({eventId:crypto.randomUUID(),timestamp:new Date().toISOString(),agentName:e.agentName,guardMode:"auto-optimize",decision:n.action==="retry"?"optimized":n.action==="block"?"block":"notify",triggeredRules:e.guardResult.triggeredRules,contextAnalysis:e.guardResult.contextAnalysis??void 0,optimization:{action:n.action,tokensBefore:s,costBefore:a,description:e.guardResult.suggestion},suggestion:e.guardResult.suggestion}),n}catch{return {action:"notify"}}}record(e){let r={...e,eventId:crypto.randomUUID(),timestamp:new Date().toISOString(),orgId:this.orgId,projectId:this.projectId};this.buffer.push(r),e.agentName&&e.costMicrodollars>0&&this.trackHourlyCost(e.agentName,e.costMicrodollars),this.buffer.length>=this.batchSize&&this.flush();}recordGuardEvent(e){this.guardBuffer.push(e);}async flush(){if(!(this.buffer.length===0&&this.guardBuffer.length===0||this.flushing)){if(this.flushing=true,this.buffer.length>0){let e=this.buffer.splice(0,this.batchSize);try{(await fetch(`${this.endpoint}/v1/events`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:JSON.stringify({batch:e})})).ok||this.buffer.unshift(...e);}catch{this.buffer.unshift(...e);}}if(this.guardBuffer.length>0){let e=this.guardBuffer.splice(0,this.batchSize);try{await fetch(`${this.endpoint}/v1/guard-events`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`},body:JSON.stringify({batch:e})});}catch{}}this.flushing=false;}}destroy(){this.timer&&(clearInterval(this.timer),this.timer=null),this.flush();}startAutoFlush(){this.timer=setInterval(()=>{this.flush();},this.flushIntervalMs),this.timer&&typeof this.timer=="object"&&"unref"in this.timer&&this.timer.unref();}};
7
+ export{m as MODEL_CONTEXT_LIMITS,T as NeuraMeter,d as NeuraMeterGuardError,p as Trace,f as analyzeContext,c as calculateCostMicrodollars,k as checkGuards,_ as estimateTokens,x as getModelContextLimit,l as getModelPricing,y as sendSlackNotification};//# sourceMappingURL=index.js.map
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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'>) => void;\n\n constructor(\n opts: TraceOptions,\n recordFn: (event: Omit<CostEvent, 'eventId' | 'timestamp' | 'orgId' | 'projectId'>) => 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://ingest.meter.neuria.tech';\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'>): 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 };\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 destroy(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n void 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/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@neurameter/core",
3
+ "version": "0.1.0",
4
+ "description": "Core SDK for NeuraMeter — AI agent cost tracking",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": ["dist"],
22
+ "scripts": {
23
+ "build": "tsup",
24
+ "dev": "tsup --watch",
25
+ "typecheck": "tsc --noEmit",
26
+ "test": "vitest run"
27
+ },
28
+ "devDependencies": {
29
+ "tsup": "^8.4.0",
30
+ "vitest": "^3.0.0"
31
+ },
32
+ "sideEffects": false,
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/neuria-dev/neurameter.git",
37
+ "directory": "packages/core"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ }
42
+ }