@loopstack/quota 0.22.2 → 0.22.3
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 +106 -0
- package/package.json +23 -2
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @loopstack/quota
|
|
2
|
+
|
|
3
|
+
> A module for the [Loopstack AI](https://loopstack.ai) automation framework.
|
|
4
|
+
|
|
5
|
+
Opt-in quota tracking and enforcement for Loopstack tool calls. Backed by Redis; ships with calculators for AI token usage and processing time.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
Loopstack workflows can call arbitrarily many tools, and some of those tools (especially AI-generation ones) incur real cost. The Quota module gives you a consistent way to meter those costs and optionally reject calls once a user or workspace hits a limit. It hooks in as a NestJS interceptor, so you don't need to thread quota checks through your tool code.
|
|
10
|
+
|
|
11
|
+
By using this module you'll get:
|
|
12
|
+
|
|
13
|
+
- **`QuotaInterceptor`** — automatically discovered via `@UseToolInterceptor()`; runs on every tool call
|
|
14
|
+
- **`QuotaCalculatorRegistry`** — maps tool names to calculator implementations; ships with `AiGenerateTextQuotaCalculator` registered for `AiGenerateText`, `AiGenerateObject`, `AiGenerateDocument`, `ClaudeGenerateText`, `ClaudeGenerateObject`, `ClaudeGenerateDocument`
|
|
15
|
+
- **`AiGenerateTextQuotaCalculator`** — computes cost from Claude token usage (input / output tokens)
|
|
16
|
+
- **`ProcessingTimeQuotaCalculator`** — computes cost from wall-clock time
|
|
17
|
+
- **`QuotaClientService`** — increment / read quota counters directly if you need custom logic
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npm install @loopstack/quota
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Register the module globally using `forRoot` or `forRootAsync`:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { QuotaModule } from '@loopstack/quota';
|
|
29
|
+
|
|
30
|
+
@Module({
|
|
31
|
+
imports: [
|
|
32
|
+
QuotaModule.forRoot({
|
|
33
|
+
enabled: true,
|
|
34
|
+
redisHost: 'localhost',
|
|
35
|
+
redisPort: 6379,
|
|
36
|
+
redisPassword: process.env.REDIS_PASSWORD,
|
|
37
|
+
}),
|
|
38
|
+
],
|
|
39
|
+
})
|
|
40
|
+
export class AppModule {}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or read configuration from env vars:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
imports: [QuotaModule.forRootAsync()];
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`forRootAsync()` reads `QUOTA_ENABLED`, `QUOTA_REDIS_HOST` (or `REDIS_HOST`), `QUOTA_REDIS_PORT` (or `REDIS_PORT`), and `QUOTA_REDIS_PASSWORD` (or `REDIS_PASSWORD`).
|
|
50
|
+
|
|
51
|
+
When `enabled: false` (the default), the Redis connection is skipped and the interceptor becomes a no-op — safe to leave wired up in dev and local setups.
|
|
52
|
+
|
|
53
|
+
## How It Works
|
|
54
|
+
|
|
55
|
+
### Registering a calculator for your own tool
|
|
56
|
+
|
|
57
|
+
Inject the registry and register a calculator from any module that runs `onModuleInit`:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { Module, OnModuleInit } from '@nestjs/common';
|
|
61
|
+
import { QuotaCalculatorRegistry, ToolQuotaCalculator } from '@loopstack/quota';
|
|
62
|
+
|
|
63
|
+
class MyCustomQuotaCalculator implements ToolQuotaCalculator {
|
|
64
|
+
calculate(result: unknown): number {
|
|
65
|
+
return 1; // e.g. 1 quota unit per call
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@Module({})
|
|
70
|
+
export class MyModule implements OnModuleInit {
|
|
71
|
+
constructor(private readonly registry: QuotaCalculatorRegistry) {}
|
|
72
|
+
|
|
73
|
+
onModuleInit() {
|
|
74
|
+
this.registry.register('MyCustomTool', new MyCustomQuotaCalculator());
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Reading current usage
|
|
80
|
+
|
|
81
|
+
Inject `QuotaClientService` (or the `QUOTA_CLIENT_SERVICE` token) and query the counter for a user / workspace identifier — see `src/services/quota-client.service.ts` for the method surface.
|
|
82
|
+
|
|
83
|
+
## Public API
|
|
84
|
+
|
|
85
|
+
- **Module:** `QuotaModule.forRoot(options)`, `QuotaModule.forRootAsync()`
|
|
86
|
+
- **Services:** `QuotaClientService`, `QuotaCalculatorRegistry`
|
|
87
|
+
- **Interceptor:** `QuotaInterceptor` (auto-wired)
|
|
88
|
+
- **Calculators:** `AiGenerateTextQuotaCalculator`, `ProcessingTimeQuotaCalculator`
|
|
89
|
+
- **Tokens:** `QUOTA_CLIENT_SERVICE`, `QUOTA_REDIS`
|
|
90
|
+
|
|
91
|
+
## Dependencies
|
|
92
|
+
|
|
93
|
+
- `@loopstack/common` — types and decorators
|
|
94
|
+
- `ioredis` — Redis client
|
|
95
|
+
- `@nestjs/common` — DI
|
|
96
|
+
|
|
97
|
+
## About
|
|
98
|
+
|
|
99
|
+
Author: [Jakob Klippel](https://www.linkedin.com/in/jakob-klippel/)
|
|
100
|
+
|
|
101
|
+
License: MIT
|
|
102
|
+
|
|
103
|
+
### Additional Resources
|
|
104
|
+
|
|
105
|
+
- [Loopstack Documentation](https://loopstack.ai/docs)
|
|
106
|
+
- Find more Loopstack modules in the [Loopstack Registry](https://loopstack.ai/registry)
|
package/package.json
CHANGED
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
"name": "@loopstack/quota",
|
|
3
3
|
"displayName": "Loopstack Quota Module",
|
|
4
4
|
"description": "Opt-in quota tracking and enforcement for Loopstack workflows",
|
|
5
|
-
"
|
|
5
|
+
"keywords": [
|
|
6
|
+
"loopstack",
|
|
7
|
+
"quota",
|
|
8
|
+
"rate-limiting",
|
|
9
|
+
"redis",
|
|
10
|
+
"workflow"
|
|
11
|
+
],
|
|
12
|
+
"version": "0.22.3",
|
|
6
13
|
"license": "MIT",
|
|
7
14
|
"author": {
|
|
8
15
|
"name": "Jakob Klippel",
|
|
@@ -10,6 +17,9 @@
|
|
|
10
17
|
},
|
|
11
18
|
"main": "dist/index.js",
|
|
12
19
|
"types": "dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": "./dist/index.js"
|
|
22
|
+
},
|
|
13
23
|
"scripts": {
|
|
14
24
|
"build": "nest build",
|
|
15
25
|
"compile": "tsc --noEmit",
|
|
@@ -19,7 +29,7 @@
|
|
|
19
29
|
"watch": "nest build --watch"
|
|
20
30
|
},
|
|
21
31
|
"dependencies": {
|
|
22
|
-
"@loopstack/common": "^0.
|
|
32
|
+
"@loopstack/common": "^0.28.0",
|
|
23
33
|
"@nestjs/common": "^11.1.19",
|
|
24
34
|
"ioredis": "^5.10.1"
|
|
25
35
|
},
|
|
@@ -49,5 +59,16 @@
|
|
|
49
59
|
"coverageDirectory": "../coverage",
|
|
50
60
|
"testEnvironment": "node",
|
|
51
61
|
"maxWorkers": 1
|
|
62
|
+
},
|
|
63
|
+
"loopstack": {
|
|
64
|
+
"modules": [
|
|
65
|
+
{
|
|
66
|
+
"path": "src/quota.module.ts",
|
|
67
|
+
"className": "QuotaModule"
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"installModes": [
|
|
71
|
+
"install"
|
|
72
|
+
]
|
|
52
73
|
}
|
|
53
74
|
}
|