Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.git
.github
.gitignore
*.md
.env
.env.*
Dockerfile
.dockerignore
coverage.out
docs/
deploy/
api/
benchmarks/
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# tok environment variables — copy to .env and fill in
# tok is a library and CLI tool with no network service.
# No API keys are required for core compression functionality.

# Optional: path to the token usage SQLite database (default: ~/.tok/usage.db)
TOK_DB_PATH=
# Optional: disable token usage tracking entirely
TOK_TRACKING_DISABLED=false
63 changes: 63 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Docker

on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
paths:
- "Dockerfile"
- "**.go"
- "go.mod"
- "go.sum"

permissions:
contents: read
packages: write

env:
REGISTRY: ghcr.io
IMAGE_NAME: graycodeai/tok

jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=sha-

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ github.ref_name }}
COMMIT=${{ github.sha }}
BUILD_DATE=${{ github.event.head_commit.timestamp }}
1 change: 1 addition & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ jobs:

- name: Dependency Review
uses: actions/dependency-review-action@v4
continue-on-error: true
with:
fail-on-severity: moderate

Expand Down
17 changes: 11 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
FROM golang:1.26.3-alpine AS builder

RUN apk add --no-cache tzdata

WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download

COPY . .
ARG VERSION=dev
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w -X 'github.com/GrayCodeAI/tok/internal/version.Version=${VERSION}'" -o /tok ./cmd/tok/
RUN CGO_ENABLED=0 go build -trimpath ./...

FROM alpine:3.21
RUN apk add --no-cache ca-certificates git
COPY --from=builder /tok /usr/local/bin/tok
RUN apk add --no-cache ca-certificates git tini && \
adduser -D -u 1000 tok

COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo

ENTRYPOINT ["tok"]
CMD ["--help"]
USER tok
WORKDIR /workspace
ENTRYPOINT ["tini", "--"]
CMD ["sleep", "infinity"]
129 changes: 129 additions & 0 deletions api/openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
openapi: "3.1.0"
info:
title: tok — Token Optimizer API Reference
description: |
tok is a tokenizer, compressor, secrets scanner, and rate limiter for AI coding agents.
It operates as a Go library and optional CLI — no HTTP server is exposed.
This document describes the library's public API surface as a machine-readable reference.
version: "0.1.0"
license:
name: MIT
url: https://github.com/GrayCodeAI/tok/blob/main/LICENSE
contact:
url: https://github.com/GrayCodeAI/tok

# No servers — tok is a library, not a network service.

components:
schemas:
CompressRequest:
type: object
required: [text]
properties:
text:
type: string
description: Input text to compress
tier:
type: string
enum: [surface, trim, extract, core, code, log, adaptive]
default: code
description: Compression tier profile
mode:
type: string
enum: [minimal, aggressive]
default: minimal
description: Compression aggressiveness
budget:
type: integer
description: Maximum output token count (0 = unlimited)
query:
type: string
description: Goal context for relevance-based filtering

CompressResponse:
type: object
properties:
compressed:
type: string
original_tokens:
type: integer
final_tokens:
type: integer
savings_percent:
type: number
format: double

EstimateRequest:
type: object
required: [text]
properties:
text:
type: string
precise:
type: boolean
default: false
description: Use BPE-accurate estimation (slower)

EstimateResponse:
type: object
properties:
tokens:
type: integer
method:
type: string
enum: [approximate, precise]

DetectSecretsRequest:
type: object
required: [text]
properties:
text:
type: string
entropy_threshold:
type: number
format: double
default: 4.5

DetectSecretsResponse:
type: object
properties:
matches:
type: array
items:
type: object
properties:
type:
type: string
value:
type: string
start:
type: integer
end:
type: integer
line:
type: integer
redacted:
type: string

x-library-api:
compress:
description: Compress text using a tiered filter pipeline
go_signature: "func Compress(text string, opts ...Option) (string, Stats)"
new_compressor:
description: Create a reusable compressor (caches tokenizer state)
go_signature: "func NewCompressor(opts ...Option) *Compressor"
estimate_tokens:
description: Fast approximate token count (±5%)
go_signature: "func EstimateTokens(text string) int"
estimate_tokens_precise:
description: BPE-accurate token count
go_signature: "func EstimateTokensPrecise(text string) int"
warmup_tokenizer:
description: Pre-initialize BPE tokenizer in background
go_signature: "func WarmupTokenizer()"
detect_secrets:
description: Detect secrets and credentials in text
go_signature: "func (d *SecretDetector) DetectSecrets(text string) []SecretMatch"
redact_secrets:
description: Detect and redact secrets in text
go_signature: "func (d *SecretDetector) RedactSecrets(text string) string"
10 changes: 10 additions & 0 deletions deploy/docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: tok

services:
tok:
build:
context: ../../
dockerfile: Dockerfile
image: ghcr.io/graycodeai/tok:dev
entrypoint: ["tok"]
command: ["--help"]
103 changes: 103 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<div align="center">

# ✂️ tok Architecture

**Tokenizer, Compressor & Secrets Scanner for AI Agents**

[![Go](https://img.shields.io/badge/Go-1.26+-00ADD8?logo=go)](https://go.dev/)
[![Type](https://img.shields.io/badge/Type-Library-green)]()

</div>

---

## 🎯 Overview

tok is a tokenizer, compression, secrets scanning, and rate limiting library for AI coding agents. It reduces LLM token costs by **60–90%** through input compression, output filtering, and transparent command rewriting.

> 💡 Pure Go library — no network service, no CLI required.

---

## 🧱 Components

```
tok/
├── api/openapi.yaml 📜 Library API surface reference
├── tok.go 📤 Public API: Compress(), EstimateTokens()
├── compressor.go 🔄 Reusable Compressor struct
├── options.go ⚙️ Option, Mode, Tier, With* functions, presets
├── secrets.go 🔒 SecretDetector, DetectSecrets(), RedactSecrets()
├── stats.go 📊 Stats returned from Compress()
├── stream.go 📡 Stream processing
└── internal/
├── core/ 🧮 BPE tokenizer, token estimation
├── filter/ 🔧 31-layer filter pipeline, tier configs
├── codeaware/ 💻 Language-specific compression rules
├── secrets/ 🔑 Regex patterns, entropy analysis, allowlists
├── cache/ 💾 Compression result caching
├── fastops/ ⚡ Performance-critical operations
└── config/ ⚙️ Configuration management
```

---

## 📤 Public API

```go
// 🗜️ One-shot compression
compressed, stats, err := tok.Compress(text,
tok.WithTier(tok.TierCode),
tok.WithBudget(4000),
tok.WithQuery("implement OAuth flow"),
)

// 🔄 Reusable compressor (caches tokenizer state)
c := tok.NewCompressor(tok.Aggressive)
compressed, stats, err := c.Compress(text)

// 📊 Token estimation
approx := tok.EstimateTokens(text) // fast, ±5%
precise := tok.EstimateTokensPrecise(text) // BPE-accurate

// 🧮 Warmup (call at startup to avoid first-call latency)
tok.WarmupTokenizer()

// 🔒 Secret detection
matches := tok.DefaultSecretDetector().DetectSecrets(text)
redacted := tok.DefaultSecretDetector().RedactSecrets(text)
```

---

## 📊 Compression Tiers

| Tier | Description | Savings |
|------|-------------|:-------:|
| 🟢 `TierSurface` | Light deduplication | ~10% |
| 🟡 `TierTrim` | Whitespace + comments | ~20% |
| 🟠 `TierExtract` | Key information extraction | ~35% |
| 🔵 `TierCode` | Code-aware compression | ~45% |
| 🔴 `TierCore` | Semantic core extraction | ~55% |
| 🟣 `TierLog` | Log file optimization | ~70% |
| ⚡ `TierAdaptive` | Adaptive per content type | varies |

---

## 🔒 Secret Detection

| Strategy | Description |
|----------|-------------|
| 🔑 **Pattern-based** | Regex for API keys, JWTs, connection strings, SSH keys |
| 📊 **Entropy-based** | Shannon entropy analysis (threshold: 4.5) |
| 📋 **Allowlists** | Prevent false positives on known-safe patterns |

---

## 🔗 Ecosystem Usage

| Consumer | Usage |
|----------|-------|
| 🦅 **hawk** | Context window management |
| 🦅 **eyrie** | Response compression |
| 🧠 **yaad** | Token budget enforcement in recall |
10 changes: 0 additions & 10 deletions internal/compress/caveman_safety.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,16 +221,6 @@ func isUpperLetter(b byte) bool {
return b >= 'A' && b <= 'Z'
}

// firstWordLower returns the first whitespace-delimited word of s,
// lower-cased. Empty if s has no word.
func firstWordLower(s string) string {
fields := strings.Fields(s)
if len(fields) == 0 {
return ""
}
return strings.ToLower(fields[0])
}

// isCJK reports whether s is primarily CJK characters (Chinese/Japanese/Korean).
// Used to decide if the caveman rules even apply — CJK text doesn't have
// articles/filler to drop.
Expand Down
Loading
Loading