Token counting, model quotas, and sliding window rate limiter
Find a file
Snider aaf8d93c1d
Some checks failed
Security Scan / security (push) Has been cancelled
Test / test (push) Has been cancelled
feat(go): add service.go — Core service registration (Mantis #1336)
Wires the *RateLimiter surface into Core's service-registration
plumbing behind a *core.ServiceRuntime[Config]. NewService() factory
builds the limiter via NewWithConfig() and returns a *Service ready
for c.Service() registration.

Action handlers exposed via OnStartup:
  ratelimit.check   — opts.model + opts.tokens → bool
  ratelimit.record  — opts.model + opts.prompt_tokens + opts.output_tokens
  ratelimit.stats   — opts.model → ModelStats (or full map)
  ratelimit.decide  — opts.model + opts.tokens → Decision
  ratelimit.reset   — opts.model → clear history

WaitForCapacity (blocking) + BackgroundPrune (returns cancel func) +
Models/Iter (iterators) stay direct method calls — not IPC-friendly.

OnShutdown persists state to disk; OnStartup is idempotent via Once.

Triplets + examples included; audit verdict COMPLIANT.

Co-authored-by: Hephaestus <hephaestus@lthn.ai>
2026-05-01 16:17:16 +01:00
.core chore: add .core/ build and release configs 2026-03-06 18:52:37 +00:00
.forgejo/workflows ci: add Forgejo Actions test and security scan workflows 2026-02-23 03:28:08 +00:00
.github/workflows ci: add Core ecosystem CI workflow with CodeRabbit auto-fix 2026-03-17 14:05:50 +00:00
docs feat(ratelimit): add agent decision guidance 2026-03-30 08:16:44 +00:00
external chore(repo): refresh submodules + go.work hygiene (Phase 2 cascade unblock) 2026-05-01 09:41:01 +01:00
go feat(go): add service.go — Core service registration (Mantis #1336) 2026-05-01 16:17:16 +01:00
specs feat(ratelimit): add agent decision guidance 2026-03-30 08:16:44 +00:00
.editorconfig chore: add Go repo norms (badges, contributing, lint, taskfile, editorconfig) 2026-02-23 06:45:45 +00:00
.gitignore chore: add .core/ and .idea/ to .gitignore 2026-03-15 10:17:50 +00:00
.gitmodules refactor(go): restructure to /go/ subtree + audit COMPLIANT (Mantis #1246) 2026-05-01 03:18:51 +01:00
.golangci.yml chore: add Go repo norms (badges, contributing, lint, taskfile, editorconfig) 2026-02-23 06:45:45 +00:00
.woodpecker.yml ci: woodpecker pipeline (Go) — golangci-lint/eslint/phpstan + sonar.lthn.sh 2026-04-29 00:03:18 +01:00
AGENTS.md refactor(go): restructure to /go/ subtree + audit COMPLIANT (Mantis #1246) 2026-05-01 03:18:51 +01:00
CLAUDE.md fix(ratelimit): align module metadata and repo guidance 2026-03-27 04:23:34 +00:00
CONTRIBUTING.md fix(ratelimit): align module metadata and repo guidance 2026-03-27 04:23:34 +00:00
go.work chore(repo): refresh submodules + go.work hygiene (Phase 2 cascade unblock) 2026-05-01 09:41:01 +01:00
LICENCE chore: add EUPL-1.2 LICENCE file (UK English canonical) 2026-05-01 08:34:53 +01:00
README.md feat(ratelimit): add agent decision guidance 2026-03-30 08:16:44 +00:00
sonar-project.properties ci: woodpecker pipeline (Go) — golangci-lint/eslint/phpstan + sonar.lthn.sh 2026-04-28 23:33:32 +01:00
threats.md fix(ratelimit): close threat-model audit findings (Cerberus #922) 2026-04-25 03:36:43 +01:00

Go Reference Licence: EUPL-1.2 Go Version

go-ratelimit

Provider-agnostic sliding window rate limiter for LLM API calls. Enforces requests per minute (RPM), tokens per minute (TPM), and requests per day (RPD) quotas per model using an in-memory sliding window. Ships with default quota profiles for Gemini, OpenAI, Anthropic, and a local inference provider. State persists across process restarts via YAML (single-process) or SQLite (multi-process, WAL mode). Includes a Gemini-specific token counting helper and a YAML-to-SQLite migration path.

Module: dappco.re/go/core/go-ratelimit Licence: EUPL-1.2 Language: Go 1.26

Quick Start

import "dappco.re/go/core/go-ratelimit"

// YAML backend (default, single-process)
rl, err := ratelimit.New()
if err != nil {
    log.Fatal(err)
}

// SQLite backend (multi-process)
rl, err = ratelimit.NewWithSQLite("/tmp/ratelimits.db")
if err != nil {
    log.Fatal(err)
}
defer rl.Close()

if rl.CanSend("gemini-2.0-flash", 1500) {
    rl.RecordUsage("gemini-2.0-flash", 1000, 500)
}

if err := rl.Persist(); err != nil {
    log.Fatal(err)
}

For agent workflows, Decide returns a structured verdict with retry guidance:

decision := rl.Decide("gemini-2.0-flash", 1500)
if !decision.Allowed {
    log.Printf("throttled (%s); retry after %s", decision.Code, decision.RetryAfter)
}

Documentation

  • Architecture — sliding window algorithm, provider quotas, YAML and SQLite backends
  • Development Guide — prerequisites, test patterns, coding standards
  • Project History — completed phases with commit hashes, known limitations

Build & Test

go build ./...
go test ./...
go test -race ./...
go vet ./...
go test -cover ./...
go mod tidy

Licence

European Union Public Licence 1.2.