Skip to content

Repository files navigation

Sayay

AI Agent Cost Guardrails
Budget enforcement middleware for LLM calls. Prevent runaway AI costs.

Quick Start · Actions · Storage · Ecosystem

LicenseTypeScriptZero depsPRs


What Is Sayay?

Sayay (Quechua: "to stop/detain") stops your AI costs from running away. Set per-user daily/monthly budgets or credit systems. Before every LLM call, Sayay decides: allow, warn, degrade, or block.

import{SayayGuard,MemoryStorage}from'sayay';constguard=newSayayGuard({storage: newMemoryStorage(),budget: {dailyUsd: 5.00,monthlyUsd: 50.00},onExceeded: 'block',degradeToModel: 'meta-llama/llama-3.3-70b-instruct:free',});// Before LLM call:constdecision=awaitguard.check('user-123',0.003);if(decision.action==='block'){thrownewError(`Budget exceeded: ${decision.reason}`);}if(decision.action==='degrade'){// Use decision.suggestedModel instead of expensive model}// After LLM call:awaitguard.record('user-123',0.0025);

Install

npm install github:breakingthecloud/sayay

Quick Start

import{SayayGuard,MemoryStorage}from'sayay';constguard=newSayayGuard({storage: newMemoryStorage(),budget: {dailyUsd: 5.00,monthlyUsd: 50.00},onExceeded: 'block',degradeToModel: 'meta-llama/llama-3.3-70b-instruct:free',});constdecision=awaitguard.check('user-123',0.003);console.log(decision.action);// 'allow' | 'warn' | 'degrade' | 'block'

Actions

ActionWhat happens
allowCall proceeds normally
warnCall proceeds, but threshold reached (log it)
degradeCall proceeds with cheaper model (decision.suggestedModel)
blockCall rejected, return error to user

Thresholds

0%────────80%──────95%──────100%
allow │ warn │degrade│ block

Configurable via warnThreshold and degradeThreshold.

Credit-Based System

constguard=newSayayGuard({storage: newMemoryStorage(),budget: {credits: 50,creditsPerCall: 1},onExceeded: 'block',warnThreshold: 80,});awaitguard.record('user-123',0,1);constusage=awaitguard.getUsage('user-123');console.log(`Credits used: ${usage.credits}/50`);

Storage Adapters

Sayay needs a storage backend to track usage. Built-in: MemoryStorage (testing) and DynamoStorage (DynamoDB, optional AWS dependency).

For production, implement SayayStorage:

// Cloudflare KV example:classKVStorageimplementsSayayStorage{constructor(privatekv: KVNamespace){}asyncget(key: string){returnparseFloat(awaitthis.kv.get(key)||'0');}asyncincrement(key: string,amount: number,ttl?: number){constcurrent=awaitthis.get(key);constnewVal=current+amount;awaitthis.kv.put(key,String(newVal),ttl ? {expirationTtl: ttl} : undefined);returnnewVal;}asyncreset(key: string){awaitthis.kv.delete(key);}}

DynamoStorage (optional)

Real-time token/cost ledger per customer/session in DynamoDB — survives Lambda warm starts and is the "Sayay = Cost Guardrail in Lambda + DynamoDB" pattern. Requires @aws-sdk/lib-dynamodb + @aws-sdk/client-dynamodb (lazy-imported, so the package keeps zero hard dependencies).

import{SayayGuard,DynamoStorage}from'@carloscortezcloud/sayay-guard';// Table: partition key `pk` (S), attribute `value` (N), TTL on `ttl` (N)constguard=newSayayGuard({storage: newDynamoStorage({tableName: 'sayay-ledger',region: 'us-east-1'}),budget: {dailyUsd: 10},});

Step Functions: TokenBudgetExceededException

Use checkOrThrow() to raise a native exception when the budget is exhausted. In AWS Step Functions, matching ErrorEquals: ["TokenBudgetExceededException"] in a Catch block instantly jumps to the error handler — stopping the workflow before retries rack up more cost.

import{SayayGuard,MemoryStorage}from'@carloscortezcloud/sayay-guard';constguard=newSayayGuard({storage: newMemoryStorage(),budget: {dailyUsd: 10}});// Throws TokenBudgetExceededException on block; returns decision otherwiseconstdecision=awaitguard.checkOrThrow('user-42',0.005);
// ASL snippet"Catch": [
{
"ErrorEquals": ["TokenBudgetExceededException"],
"Next": "HandleBudgetExceeded"
}
]

TokenBudgetExceededException extends BudgetExceededError, so existing instanceof BudgetExceededError checks keep working (backward compatible).

CloudWatch observability (optional)

Pass cloudWatch in config to emit a metric per decision. Requires @aws-sdk/client-cloudwatch (lazy-imported). Emits Decision, RemainingBudget, and UsagePercent metrics under the Sayay namespace (configurable).

constguard=newSayayGuard({
storage,budget: {dailyUsd: 10},cloudWatch: {metricNamespace: 'MyApp',region: 'us-east-1'},});

Qhaway observability (Sayay → Qhaway)

Pipe every budget decision (allow/warn/degrade/block) into Qhaway as a sayay.check span. The Qhaway metrics pipeline then exposes qhaway_sayay_decisions_total{action,user} and the Grafana "Budget Guardrails" panel visualizes blocks vs degradations vs allows.

import{SayayGuard,MemoryStorage,SayayQhawayPlugin}from'@carloscortezcloud/sayay-guard';import{MemoryStorageasQhawayStorage}from'@carloscortezcloud/qhaway';constqhawayStorage=newQhawayStorage();constqhaway=newSayayQhawayPlugin({storage: qhawayStorage,agentId: 'finops-agent',sessionId: 'session-demo-001',budgetKey: 'team-budget',});constguard=newSayayGuard({storage: newMemoryStorage(),budget: {dailyUsd: 10},onDecision: qhaway.onDecision,});

onDecision writes a span with tool_name: "sayay.check", model: "budget-guard", cost_usd: 0, and metadata: { action, budgetKey, spent, limit, usagePercent, suggestedModel }. A failed write only logs — observability never breaks the guard path.

Works with any Tinkuy agent via its guard option (see examples/README.md for the run guide and examples/sayay-qhaway.ts for the full wiring):

constagent=newAgent({ router, guard,tools: [...],systemPrompt: '...'});

Integration with Styrr

import{StyrRouter}from'styrr';import{SayayGuard,MemoryStorage}from'sayay';constguard=newSayayGuard({storage: newMemoryStorage(),budget: {dailyUsd: 10}});constrouter=newStyrRouter({apiKey: '...',models: [...]});asyncfunctionsafeLLMCall(userId: string,prompt: string){constdecision=awaitguard.check(userId,0.005);if(decision.action==='block')thrownewError(decision.reason);constresult=awaitrouter.prompt(prompt);awaitguard.record(userId,result.usage?.totalTokens||0.003);returnresult;}

Ecosystem

PackageRolenpm
SayayCost guardrails (this)GitHub
StyrrLLM routerstyrr
TinkuyAgent framework@carloscortezcloud/tinkuy-agent
QhawayAgent observability@carloscortezcloud/qhaway
TideRAGEdge RAG pipeline@carloscortezcloud/tiderag

License

Apache 2.0 — see LICENSE.


Built by engineers who got tired of surprise AWS bills.
Tinkuy Labs · finoptix.dev

Your AI costs should have a stop button. Sayay is that button.

About

AI agent cost guardrails. Budget enforcement per user/session. Block, degrade, or warn before LLM calls exceed limits.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages