From c9c7af86a0c6fdbe2127c00d608b88a40d2e5a4f Mon Sep 17 00:00:00 2001 From: reaatech <138725666+reaatech@users.noreply.github.com> Date: Thu, 30 Apr 2026 05:45:15 -0700 Subject: [PATCH 01/11] docs: overhaul package and root READMEs for uniformity Match the a2a-reference-ts README format: - Badges (npm version, License, CI) on every package README - Status note, Feature Overview bullet list, Quick Start code example - Full API Reference tables documenting constructors, configs, methods, types - Usage Patterns with practical examples - Related Packages linking to sibling packages - Root README restructured with packages table, architecture diagram, abbreviated config --- ARCHITECTURE.md | 16 +- CONTRIBUTING.md | 2 +- README.md | 264 ++++++++++---------------- packages/adapters/dynamodb/README.md | 144 +++++++++++--- packages/adapters/qdrant/README.md | 177 +++++++++++++++-- packages/adapters/redis/README.md | 126 +++++++++++-- packages/core/README.md | 250 ++++++++++++++++++++---- packages/cost-tracker/README.md | 184 ++++++++++++++++-- packages/observability/README.md | 201 ++++++++++++++++++-- packages/server/README.md | 271 ++++++++++++++++++++++++--- skills/devops/skills.md | 10 +- 11 files changed, 1322 insertions(+), 323 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index a7d578b..ba4444e 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -49,7 +49,7 @@ llm-cache is a sophisticated semantic caching layer designed to reduce LLM API c ## Core Components -### 1. Cache Engine (`@llm-cache/core`) +### 1. Cache Engine (`@reaatech/llm-cache`) The heart of the system, responsible for orchestrating cache operations. @@ -100,7 +100,7 @@ class SimilarityMatcher { - Result ranking and scoring - Use case and model filtering -### 2. Embedding Service (`@llm-cache/core/embedding`) +### 2. Embedding Service (`@reaatech/llm-cache/embedding`) Handles all embedding-related operations with cost optimization. @@ -140,7 +140,7 @@ class EmbeddingCache { } ``` -### 3. Storage Adapters (`@llm-cache/adapters/*`) +### 3. Storage Adapters (`@reaatech/llm-cache-adapters/*`) Abstract storage layer with multiple implementations. @@ -257,7 +257,7 @@ class InMemoryAdapter implements StorageAdapter { } ``` -### 4. Cost Tracker (`@llm-cache/cost-tracker`) +### 4. Cost Tracker (`@reaatech/llm-cache-cost-tracker`) Comprehensive cost tracking and savings calculation. @@ -303,7 +303,7 @@ interface ModelPricing { } ``` -### 5. Observability Service (`@llm-cache/observability`) +### 5. Observability Service (`@reaatech/llm-cache-observability`) Enterprise-grade monitoring and logging. @@ -700,15 +700,15 @@ class AuditLogger { **Primary Distribution: npm Library** -llm-cache is primarily distributed as a set of npm packages (`@llm-cache/core`, `@llm-cache/adapters-redis`, etc.) that developers import into their applications. This provides the tightest integration with existing LLM client code and the lowest latency (no network hop to a separate service). +llm-cache is primarily distributed as a set of npm packages (`@reaatech/llm-cache`, `@reaatech/llm-cache-adapters-redis`, etc.) that developers import into their applications. This provides the tightest integration with existing LLM client code and the lowest latency (no network hop to a separate service). **Optional for Users, Required to Develop: HTTP Service Wrapper** -A thin HTTP wrapper is provided as `@llm-cache/server`. Users can choose to import `@llm-cache/core` directly into their application (lowest latency, tightest integration) OR deploy `@llm-cache/server` as a sidecar/centralized service (polyglot environments, service-oriented architectures). +A thin HTTP wrapper is provided as `@reaatech/llm-cache-server`. Users can choose to import `@reaatech/llm-cache` directly into their application (lowest latency, tightest integration) OR deploy `@reaatech/llm-cache-server` as a sidecar/centralized service (polyglot environments, service-oriented architectures). The server package is **optional for end users** but is a **required workspace package to develop and maintain** — it must be built, tested, and released in lockstep with core releases. -The Docker, Kubernetes, and Helm configurations described below apply to the `@llm-cache/server` service wrapper. +The Docker, Kubernetes, and Helm configurations described below apply to the `@reaatech/llm-cache-server` service wrapper. --- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0b6a312..1d960ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -161,7 +161,7 @@ pnpm test pnpm test:coverage # Specific package tests -pnpm test --filter=@llm-cache/core +pnpm test --filter=@reaatech/llm-cache # Watch mode pnpm test:watch diff --git a/README.md b/README.md index fe292a3..0c03d30 100644 --- a/README.md +++ b/README.md @@ -1,74 +1,95 @@ # llm-cache -
+[](https://github.com/reaatech/llm-cache/actions/workflows/ci.yml) +[](LICENSE) +[](https://www.typescriptlang.org/) -Semantic caching layer for LLM calls — embedding-based similarity matching, not exact-match alone. Reduce latency, cut API costs, and maintain consistent responses across semantically equivalent prompts. +> Semantic caching layer for LLM calls — embedding-based similarity matching with model-aware fingerprinting, use-case segmentation, adaptive TTL, and cost tracking. Reduce latency, cut API costs, and maintain consistent responses across semantically equivalent prompts. + +This monorepo provides the core caching engine, pluggable storage adapters, an HTTP server wrapper, and supporting packages for cost tracking and observability. ## Features -- **Exact-match cache** — SHA-256 hash of the full prompt for zero-latency cache hits -- **Semantic cache** — Embed prompts and search for similar cached entries above a configurable cosine similarity threshold -- **Cache fingerprinting** — Model, temperature, top_p, system prompt, and tools are hashed together so different generation configurations never collide +- **Exact-match cache** — SHA-256 hash of the full prompt for sub-millisecond cache hits +- **Semantic cache** — Embed prompts and search for similar entries above a configurable cosine similarity threshold +- **Generation config fingerprinting** — Model, temperature, top_p, system prompt, and tools are hashed so different configurations never collide - **Multi-adapter storage** — Pluggable backends for metadata (Memory, Redis, DynamoDB) and vector search (Memory, Qdrant) -- **Use case segmentation** — Isolate caches by use case (e.g., summarization vs. classification) to prevent cross-contamination -- **Cost tracking** — Built-in pricing for 40+ models across OpenAI, Anthropic, and Google; calculate dollars saved per cache hit +- **Use-case segmentation** — Isolate caches by use case to prevent cross-contamination - **Adaptive TTL** — Factual queries expire faster than creative ones; sensitive data gets the shortest TTL -- **Observability** — Structured JSON logging, Prometheus-compatible metrics, and optional distributed tracing support +- **Cost tracking** — Built-in pricing for 40+ models across OpenAI, Anthropic, and Google with savings calculation +- **Observability** — Structured JSON logging with automatic PII redaction and Prometheus-compatible metrics - **Encryption-ready** — AES-256-GCM for prompts, responses, and embeddings at the storage layer -- **HTTP server** — Optional REST API wrapper for polyglot and service-oriented architectures +- **HTTP server** — REST API wrapper for polyglot and service-oriented architectures ## Installation +### Using the packages + +Packages are published under the `@reaatech` scope and can be installed individually: + ```bash # Core library (required) -pnpm add @llm-cache/core +pnpm add @reaatech/llm-cache -# Storage adapters (optional — pick what you need) -pnpm add @llm-cache/adapters-redis # Redis for exact-match metadata -pnpm add @llm-cache/adapters-dynamodb # DynamoDB for exact-match metadata -pnpm add @llm-cache/adapters-qdrant # Qdrant for vector search +# Storage adapters (pick what you need) +pnpm add @reaatech/llm-cache-adapters-redis # Redis for exact-match metadata +pnpm add @reaatech/llm-cache-adapters-dynamodb # DynamoDB for exact-match metadata +pnpm add @reaatech/llm-cache-adapters-qdrant # Qdrant for vector search # Utilities (optional) -pnpm add @llm-cache/cost-tracker # Cost calculation and pricing data -pnpm add @llm-cache/observability # Metrics, logging, and tracing -pnpm add @llm-cache/server # HTTP server wrapper +pnpm add @reaatech/llm-cache-cost-tracker # Cost calculation and pricing data +pnpm add @reaatech/llm-cache-observability # Metrics, logging, and tracing +pnpm add @reaatech/llm-cache-server # HTTP server wrapper ``` -**Requirements:** Node.js >= 20.0.0, pnpm >= 8.0.0 +### Contributing -## Quick Start +```bash +# Clone the repository +git clone https://github.com/reaatech/llm-cache.git +cd llm-cache + +# Install dependencies +pnpm install + +# Build all packages +pnpm build + +# Run the test suite +pnpm test -### Library Usage +# Run linting +pnpm lint + +# Run type check +pnpm typecheck +``` + +## Quick Start ```typescript -import { CacheEngine, InMemoryAdapter, OpenAIEmbedder } from '@llm-cache/core'; +import { CacheEngine, InMemoryAdapter, OpenAIEmbedder } from "@reaatech/llm-cache"; const cache = new CacheEngine({ storage: new InMemoryAdapter(), vectorStorage: new InMemoryAdapter(), embedder: new OpenAIEmbedder({ - provider: 'openai', - model: 'text-embedding-3-small', + provider: "openai", + model: "text-embedding-3-small", dimensions: 1536, apiKey: process.env.OPENAI_API_KEY, }), config: { - storage: { adapter: 'memory' }, - vectorStorage: { adapter: 'memory' }, + storage: { adapter: "memory" }, + vectorStorage: { adapter: "memory" }, embedding: { - provider: 'openai', - model: 'text-embedding-3-small', + provider: "openai", + model: "text-embedding-3-small", dimensions: 1536, batchSize: 100, maxRetries: 3, }, - similarity: { threshold: 0.8, metric: 'cosine', maxResults: 10 }, + similarity: { threshold: 0.8, metric: "cosine", maxResults: 10 }, ttl: { default: 3600, factual: 1800, @@ -77,59 +98,57 @@ const cache = new CacheEngine({ sensitive: 600, byUseCase: {}, }, - segmentation: { enabled: true, defaultUseCase: 'general' }, - cost: { enabled: true, currency: 'USD' }, - observability: { metrics: true, tracing: false, logging: 'info' }, + segmentation: { enabled: true, defaultUseCase: "general" }, + cost: { enabled: true, currency: "USD" }, + observability: { metrics: true, tracing: false, logging: "info" }, }, }); // Store a response await cache.set( - 'What is TypeScript?', - { choices: [{ message: { content: 'A typed superset of JavaScript' } }] }, - { model: 'gpt-4', modelVersion: 'gpt-4-0613' }, + "What is TypeScript?", + { choices: [{ message: { content: "A typed superset of JavaScript" } }] }, + { model: "gpt-4", modelVersion: "gpt-4-0613" }, ); // Exact match — < 1ms -const exact = await cache.get('What is TypeScript?', { - model: 'gpt-4', - modelVersion: 'gpt-4-0613', +const exact = await cache.get("What is TypeScript?", { + model: "gpt-4", + modelVersion: "gpt-4-0613", }); -// { hit: true, type: 'exact', entry: {...} } +// → { hit: true, type: "exact", entry: {...} } // Semantic match — < 50ms with Qdrant -const semantic = await cache.get('Tell me about TypeScript', { - model: 'gpt-4', - modelVersion: 'gpt-4-0613', +const semantic = await cache.get("Tell me about TypeScript", { + model: "gpt-4", + modelVersion: "gpt-4-0613", }); -// { hit: true, type: 'semantic', confidence: 0.92, entry: {...} } - -// Miss — forward to your LLM provider, then cache the response -const miss = await cache.get('What is Rust?', { - model: 'gpt-4', - modelVersion: 'gpt-4-0613', -}); -// { hit: false } +// → { hit: true, type: "semantic", confidence: 0.92, entry: {...} } ``` ### Server Usage (Docker) ```bash -# Start Qdrant + Redis + cache server docker compose up -# Check cache curl -X POST http://localhost:3000/cache/get \ - -H 'Content-Type: application/json' \ + -H "Content-Type: application/json" \ -d '{"prompt": "What is TypeScript?", "options": {"model": "gpt-4", "modelVersion": "gpt-4-0613"}}' - -# Store a response -curl -X POST http://localhost:3000/cache/set \ - -H 'Content-Type: application/json' \ - -d '{"prompt": "What is TypeScript?", "response": {"choices": [{"message": {"content": "A typed superset of JavaScript"}}]}, "options": {"model": "gpt-4", "modelVersion": "gpt-4-0613"}}' ``` -For end-to-end examples with Redis, Qdrant, and DynamoDB, see the [`examples/`](examples/) directory. +See the [`examples/`](examples/) directory for end-to-end examples with Redis, Qdrant, and DynamoDB. + +## Packages + +| Package | Description | +| ------- | ----------- | +| [`@reaatech/llm-cache`](./packages/core) | Core caching engine, adapters (InMemory), embedder (OpenAI), similarity matcher, and all shared types | +| [`@reaatech/llm-cache-adapters-redis`](./packages/adapters/redis) | Redis storage adapter with automatic TTL, connection pooling, and batch operations | +| [`@reaatech/llm-cache-adapters-dynamodb`](./packages/adapters/dynamodb) | DynamoDB adapter with native TTL, GSIs for metadata queries, and batch operations | +| [`@reaatech/llm-cache-adapters-qdrant`](./packages/adapters/qdrant) | Qdrant vector database adapter for low-latency semantic search via HNSW | +| [`@reaatech/llm-cache-cost-tracker`](./packages/cost-tracker) | Cost calculator with built-in pricing for 40+ models and savings computation | +| [`@reaatech/llm-cache-observability`](./packages/observability) | Structured JSON logger with PII redaction and Prometheus metrics collector | +| [`@reaatech/llm-cache-server`](./packages/server) | HTTP server wrapper with configurable storage and vector adapters | ## Architecture @@ -157,108 +176,29 @@ CacheEngine 3. **Semantic search** — Embed the prompt, query the vector store for similar entries above the configured threshold (< 50ms with Qdrant) 4. **Cache miss** — Forward to your LLM provider, then store the result for future hits -## Packages - -| Package | npm | Description | -|---|---|---| -| `@llm-cache/core` | [](https://www.npmjs.com/package/@llm-cache/core) | CacheEngine, adapters (InMemory), embedder (OpenAI), similarity matcher, and all shared types | -| `@llm-cache/adapters-redis` | [](https://www.npmjs.com/package/@llm-cache/adapters-redis) | Redis storage adapter with connection pooling, SETEX TTL, and key-space scanning | -| `@llm-cache/adapters-dynamodb` | [](https://www.npmjs.com/package/@llm-cache/adapters-dynamodb) | DynamoDB adapter with native TTL, GSIs for useCase and modelVersion queries, batch operations | -| `@llm-cache/adapters-qdrant` | [](https://www.npmjs.com/package/@llm-cache/adapters-qdrant) | Qdrant vector database adapter for low-latency semantic search via HNSW | -| `@llm-cache/cost-tracker` | [](https://www.npmjs.com/package/@llm-cache/cost-tracker) | Cost calculator with built-in pricing for 40+ models (OpenAI, Anthropic, Google) | -| `@llm-cache/observability` | [](https://www.npmjs.com/package/@llm-cache/observability) | Structured JSON logger, Prometheus metrics collector, optional tracing hooks | -| `@llm-cache/server` | [](https://www.npmjs.com/package/@llm-cache/server) | HTTP server wrapper with configurable storage and vector adapters | - ## Configuration -### Environment Variables +See [`.env.example`](.env.example) for the full annotated configuration reference. Core environment variables: | Variable | Description | Default | -|---|---|---| +|----------|-------------|---------| | `OPENAI_API_KEY` | OpenAI API key for embeddings | — | -| `OPENAI_ORGANIZATION` | OpenAI organization ID (optional) | — | -| `EMBEDDING_PROVIDER` | Embedding provider (`openai`) | `openai` | -| `OPENAI_EMBEDDING_MODEL` | Embedding model name | `text-embedding-3-small` | -| `OPENAI_EMBEDDING_DIMENSIONS` | Embedding vector dimensions | `1536` | -| `EMBEDDING_BATCH_SIZE` | Max prompts per embedding API call | `100` | -| `EMBEDDING_MAX_RETRIES` | Max retries on embedding API failures | `3` | -| `STORAGE_ADAPTER` | Metadata storage backend | `memory` | -| `REDIS_URL` | Redis connection URL (e.g. `redis://localhost:6379`) | — | -| `DYNAMODB_REGION` | AWS region for DynamoDB | — | -| `DYNAMODB_TABLE` | DynamoDB table name | — | -| `DYNAMODB_ENDPOINT` | DynamoDB endpoint override (local dev) | — | -| `VECTOR_STORAGE_ADAPTER` | Vector search backend | `memory` | -| `QDRANT_URL` | Qdrant server URL | — | -| `QDRANT_COLLECTION` | Qdrant collection name | `llm-cache` | -| `QDRANT_API_KEY` | Qdrant API key (optional) | — | +| `STORAGE_ADAPTER` | Metadata storage backend (`memory`, `redis`, `dynamodb`) | `memory` | +| `VECTOR_STORAGE_ADAPTER` | Vector search backend (`memory`, `qdrant`) | `memory` | | `SIMILARITY_THRESHOLD` | Cosine similarity threshold (0.0–1.0) | `0.8` | -| `SIMILARITY_MAX_RESULTS` | Max results from semantic search | `10` | | `TTL_DEFAULT` | Default cache TTL in seconds | `3600` | -| `TTL_FACTUAL` | TTL for factual queries | `1800` | -| `TTL_CREATIVE` | TTL for creative queries | `7200` | -| `TTL_ANALYTICAL` | TTL for analytical queries | `3600` | -| `TTL_SENSITIVE` | TTL for sensitive data | `600` | -| `SEGMENTATION_ENABLED` | Enable use-case-based cache isolation | `true` | -| `DEFAULT_USE_CASE` | Default use case when none specified | `general` | -| `COST_TRACKING_ENABLED` | Enable cost savings calculation | `true` | -| `COST_CURRENCY` | Currency for cost reporting | `USD` | -| `METRICS_ENABLED` | Enable Prometheus metrics collection | `true` | -| `TRACING_ENABLED` | Enable distributed tracing hooks | `false` | -| `LOG_LEVEL` | Log level (`error`, `warn`, `info`, `debug`) | `info` | | `LLM_CACHE_API_KEY` | API key for server authentication | — | | `PORT` | HTTP server port | `3000` | -| `MAX_BODY_BYTES` | Max request body size (bytes) | `1048576` | - -See [`.env.example`](.env.example) for the full annotated configuration reference. - -### Similarity Threshold Tuning - -| Threshold | Behavior | Recommended For | -|---|---|---| -| `0.95+` | Near-identical matches only | Strict fact retrieval, legal text | -| `0.85–0.94` | Close paraphrases | Q&A, documentation search | -| `0.75–0.84` | Semantically related | Summarization, creative writing | -| `0.70–0.74` | Loosely related | Brainstorming, exploration | ## Operational Notes -- **DynamoDB TTL** — Enable native TTL on the `expiresAtEpoch` attribute (override via the `ttlAttribute` adapter option). Without it, expired rows accumulate indefinitely. -- **Qdrant eviction** — The adapter does not auto-evict expired points. Run `cache.invalidate({ olderThan })` periodically to clean up. -- **Server authentication** — Set `LLM_CACHE_API_KEY` before exposing the server beyond a trusted network. Without it, all `/cache/*` endpoints are unauthenticated. -- **Pricing data** — Pricing in `@llm-cache/cost-tracker` is provided as reference and may lag provider price changes. Verify against your provider before relying on it for billing. -- **Redis SCAN queries** — `findByUseCase`, `findByModelVersion`, and `invalidateByCriteria` walk the keyspace via `SCAN` (O(N)). Avoid calling them on hot paths; run from background jobs or deploy Redis Stack with RediSearch. - -## Development - -```bash -# Install dependencies -pnpm install - -# Build all packages -pnpm build - -# Run all tests -pnpm test - -# Run tests with coverage -pnpm test:coverage - -# Run tests for a specific package -pnpm --filter @llm-cache/core test +- **DynamoDB TTL** — Enable native TTL on the `expiresAtEpoch` attribute (override via the `ttlAttribute` adapter option). +- **Qdrant eviction** — The adapter does not auto-evict expired points. Run `cache.invalidate({ olderThan })` periodically. +- **Server authentication** — Set `LLM_CACHE_API_KEY` before exposing the server beyond a trusted network. +- **Pricing data** — Pricing in `@reaatech/llm-cache-cost-tracker` is provided as reference and may lag provider price changes. +- **Redis SCAN queries** — `findByUseCase`, `findByModelVersion`, and `invalidateByCriteria` walk the keyspace via `SCAN` (O(N)). Avoid calling on hot paths. -# Lint all packages -pnpm lint -pnpm lint:fix - -# Type-check all packages -pnpm typecheck - -# Format code -pnpm format -pnpm format:check -``` - -### Project Structure +## Project Structure ``` llm-cache/ @@ -274,23 +214,19 @@ llm-cache/ ├── examples/ # Usage examples (basic, Redis, Qdrant) ├── skills/ # AI agent development skills ├── docker-compose.yml # Local development stack (Qdrant + Redis + server) -├── tsconfig.json # Root TypeScript configuration (strict, ESNext) +├── tsconfig.json # Root TypeScript configuration ├── pnpm-workspace.yaml # pnpm workspace definition └── .github/workflows/ # CI/CD pipelines ``` -## Contributing - -Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on commit conventions, testing requirements, and the pull request process. This project follows the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html) code of conduct. - -For security vulnerabilities, please report directly via [GitHub Security Advisories](https://github.com/reaatech/llm-cache/security/advisories/new) rather than opening a public issue. See [SECURITY.md](SECURITY.md) for the full disclosure policy. - ## Documentation -- [ARCHITECTURE.md](ARCHITECTURE.md) — System design, data flow, and component interfaces -- [DEV_PLAN.md](DEV_PLAN.md) — Development roadmap and milestones -- [AGENTS.md](AGENTS.md) — AI agent development framework and skill definitions +- [`ARCHITECTURE.md`](ARCHITECTURE.md) — System design, data flow, and component interfaces +- [`DEV_PLAN.md`](DEV_PLAN.md) — Development roadmap and milestones +- [`CONTRIBUTING.md`](CONTRIBUTING.md) — Contribution workflow and release process +- [`AGENTS.md`](AGENTS.md) — AI agent development framework and skill definitions +- [`SECURITY.md`](SECURITY.md) — Vulnerability reporting and security best practices ## License -MIT © [llm-cache contributors](https://github.com/reaatech/llm-cache/graphs/contributors) +[MIT](LICENSE) diff --git a/packages/adapters/dynamodb/README.md b/packages/adapters/dynamodb/README.md index f6c3cb0..cc68f56 100644 --- a/packages/adapters/dynamodb/README.md +++ b/packages/adapters/dynamodb/README.md @@ -1,45 +1,147 @@ -# @llm-cache/adapters-dynamodb +# @reaatech/llm-cache-adapters-dynamodb -DynamoDB storage adapter for llm-cache exact-match metadata storage. +[](https://www.npmjs.com/package/@reaatech/llm-cache-adapters-dynamodb) +[](https://github.com/reaatech/llm-cache/blob/main/LICENSE) +[](https://github.com/reaatech/llm-cache/actions/workflows/ci.yml) -## Install +> **Status:** Pre-1.0 — APIs may change in minor versions. Pin to a specific version in production. + +DynamoDB storage adapter for llm-cache exact-match metadata. Provides serverless, scalable persistence with native DynamoDB TTL, GSI-based metadata queries, and batch operations chunked to AWS limits. + +## Installation ```bash -npm install @llm-cache/adapters-dynamodb +npm install @reaatech/llm-cache-adapters-dynamodb +# or +pnpm add @reaatech/llm-cache-adapters-dynamodb ``` -## Usage +## Feature Overview + +- **Native DynamoDB TTL** — writes an epoch-second attribute (`expiresAtEpoch` by default) for automatic row expiration +- **GSI-backed queries** — `gsi1` indexes `useCase`, `gsi2` indexes `modelVersion` for efficient metadata queries +- **Batch operations** — `getBatch`, `setBatch`, and `deleteBatch` chunked to DynamoDB's 25-item limit +- **Paginated invalidation** — `invalidateByCriteria` uses GSI queries when possible, falling back to `Scan` for broad criteria +- **Stats** — `getStats()` returns item count and table size from `DescribeTable` +- **Health check** — `healthCheck()` runs a lightweight `Scan` (limit 1) + +## Quick Start ```typescript -import { DynamoDBAdapter } from '@llm-cache/adapters-dynamodb'; +import { CacheEngine, OpenAIEmbedder } from "@reaatech/llm-cache"; +import { DynamoDBAdapter } from "@reaatech/llm-cache-adapters-dynamodb"; -const adapter = new DynamoDBAdapter({ - region: 'us-east-1', - tableName: 'llm-cache', +const storage = new DynamoDBAdapter({ + region: "us-east-1", + tableName: "llm-cache", }); -// Use adapter with CacheEngine const cache = new CacheEngine({ - storage: adapter, - vectorStorage: /* Qdrant or InMemoryAdapter */, - embedder, - config, + storage, + vectorStorage: /* QdrantAdapter or InMemoryAdapter */, + embedder: new OpenAIEmbedder({ + provider: "openai", + model: "text-embedding-3-small", + dimensions: 1536, + apiKey: process.env.OPENAI_API_KEY, + }), + config: { /* ... */ }, }); ``` ## Table Schema -Your DynamoDB table should have: +Your DynamoDB table must have these attributes and indexes: + +| Attribute | Type | Key | Description | +|-----------|------|-----|-------------| +| `pk` | String | HASH | Exact-match key (`promptHash:generationConfigHash`) | +| `gsi1pk` | String | GSI1 HASH | `USECASE#` with a
+TOTP code or recovery code. Security key alone is not sufficient at the CLI.
+
+### `403 You cannot publish over the previously published versions`
+
+That version already exists on npm. Either:
+- The previous publish succeeded silently (check with `npm view`)
+- Or you need to bump the version first (`pnpm changeset` + version PR)
+
+### `402 Payment Required`
+
+Scoped package published as private without `--access public`. Add
+`"publishConfig": { "access": "public" }` to `package.json` and republish a new
+version (you can't change a published version's access).
+
+### `npm error code E401` / `ENEEDAUTH` in CI
+
+The `NPM_TOKEN` secret is missing, expired, or doesn't have write access. Verify
+in repo settings, regenerate if needed.
+
+### Workflow can't open Version Packages PR
+
+GitHub Actions doesn't have permission. Settings → Actions → General → Workflow
+permissions → "Read and write" + "Allow GitHub Actions to create and approve PRs."
+
+### `pnpm -r publish` says "+ " but `npm view` returns 404
+
+Either CDN propagation lag (wait 60–90 seconds) or the upload happened but the
+registry rejected after. Try `npm view --registry=https://registry.npmjs.org/`.
+If still 404, re-publish the same version — if it returns E403 "cannot publish
+over previously published," the package is actually live.
+
+### Recovery codes look like 64-char hex, not `XXXXX-XXXXX`
+
+That's correct. Use them as-is with `--otp=`.
+
+### Stale `.js` / `.d.ts` files in `packages/*/src/`
+
+Old tsup output that wrote into `src/` instead of `dist/`. Delete them and add
+the gitignore patterns from section 2.2.
+
+### `403 permission_denied: The token provided does not match expected scopes` on GitHub Packages publish
+
+The `gh` token doesn't have `write:packages`. Refresh with:
+```bash
+gh auth refresh -h github.com -s write:packages,read:packages
+```
+This is local-only — CI's `GITHUB_TOKEN` is fine because the workflow's
+`permissions:` block grants `packages: write`.
+
+### `422 Unprocessable Entity` on GitHub Packages publish
+
+`package.json`'s `repository.url` points to a different GitHub owner than the
+one publishing. Make sure `repository.url` points to a repo owned by the same
+account/org as the package scope.
+
+### GitHub repo sidebar "Packages" section is empty after CI publish
+
+Either:
+- The mirror step hasn't run yet — check that the workflow has the
+ `Mirror published packages to GitHub Packages` step and `packages: write` in
+ permissions
+- The mirror step's package-name → directory mapping is wrong (e.g.,
+ `packages/${name#@scope/prefix-}` doesn't match your naming)
+- The CI run published nothing (`steps.changesets.outputs.published == 'true'`
+ was false), so the mirror step was correctly skipped
+
+If you've published versions only manually to npm and never via CI, you need
+the one-time backfill from section 7.4 — the mirror step keys off CI publishes.
+
+---
+
+## 11. Quick command reference
+
+```bash
+# Build everything
+pnpm build
+
+# Run tests + typecheck + lint
+pnpm test && pnpm typecheck && pnpm lint
+
+# Add a changeset (interactive)
+pnpm changeset
+
+# Bump versions per pending changesets
+pnpm version-packages
+
+# Local publish (per package, with OTP)
+cd packages/
+pnpm publish --access public --no-git-checks --otp=
+
+# Verify a package is live
+curl -s -o /dev/null -w "%{http_code}\n" https://registry.npmjs.org/@scope%2fpkg
+npm view @scope/pkg version
+
+# List all published packages in your scope
+npm access list packages @scope
+
+# Refresh local gh token to allow GitHub Packages publishing (one-time)
+gh auth refresh -h github.com -s write:packages,read:packages
+
+# Manually mirror a package to GitHub Packages (one-time backfill)
+TOKEN=$(gh auth token)
+cd packages/
+echo "@scope:registry=https://npm.pkg.github.com
+//npm.pkg.github.com/:_authToken=${TOKEN}" > /tmp/.npmrc.gh
+NPM_CONFIG_USERCONFIG=/tmp/.npmrc.gh \
+ npm publish --registry=https://npm.pkg.github.com
+rm /tmp/.npmrc.gh
+
+# List packages on GitHub
+gh api "/users//packages?package_type=npm" --jq '.[].name'
+```
+
+---
+
+## 12. References
+
+- Changesets: https://github.com/changesets/changesets
+- changesets/action: https://github.com/changesets/action
+- npm scoped packages: https://docs.npmjs.com/about-scopes
+- npm provenance: https://docs.npmjs.com/generating-provenance-statements
+- pnpm publish: https://pnpm.io/cli/publish
diff --git a/biome.json b/biome.json
new file mode 100644
index 0000000..da77c68
--- /dev/null
+++ b/biome.json
@@ -0,0 +1,33 @@
+{
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
+ "files": {
+ "ignore": ["dist", "node_modules", "coverage", ".turbo"]
+ },
+ "organizeImports": {
+ "enabled": true
+ },
+ "linter": {
+ "enabled": true,
+ "rules": {
+ "recommended": true,
+ "suspicious": {
+ "noExplicitAny": "error"
+ },
+ "style": {
+ "noNonNullAssertion": "error"
+ }
+ }
+ },
+ "formatter": {
+ "enabled": true,
+ "indentStyle": "space",
+ "indentWidth": 2,
+ "lineWidth": 100
+ },
+ "javascript": {
+ "formatter": {
+ "quoteStyle": "single",
+ "trailingCommas": "all"
+ }
+ }
+}
diff --git a/examples/basic-usage.ts b/examples/basic-usage.ts
index 899db26..21bbd56 100644
--- a/examples/basic-usage.ts
+++ b/examples/basic-usage.ts
@@ -1,5 +1,5 @@
-import { CacheEngine, InMemoryAdapter, OpenAIEmbedder } from '@llm-cache/core';
-import type { CacheConfig } from '@llm-cache/core';
+import { CacheEngine, InMemoryAdapter, OpenAIEmbedder } from '@reaatech/llm-cache';
+import type { CacheConfig } from '@reaatech/llm-cache';
const config: CacheConfig = {
storage: { adapter: 'memory' },
diff --git a/examples/package.json b/examples/package.json
index a6a9dcb..a606618 100644
--- a/examples/package.json
+++ b/examples/package.json
@@ -1,5 +1,5 @@
{
- "name": "@llm-cache/examples",
+ "name": "@reaatech/llm-cache-examples",
"version": "0.1.0",
"author": "Rick Somers (https://reaatech.com)",
"private": true,
@@ -8,10 +8,10 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
- "@llm-cache/core": "workspace:*",
- "@llm-cache/adapters-redis": "workspace:*",
- "@llm-cache/adapters-qdrant": "workspace:*",
- "@llm-cache/adapters-dynamodb": "workspace:*"
+ "@reaatech/llm-cache": "workspace:*",
+ "@reaatech/llm-cache-adapters-redis": "workspace:*",
+ "@reaatech/llm-cache-adapters-qdrant": "workspace:*",
+ "@reaatech/llm-cache-adapters-dynamodb": "workspace:*"
},
"devDependencies": {
"@types/node": "^20.11.0",
diff --git a/examples/qdrant-example.ts b/examples/qdrant-example.ts
index 10ec49f..012e0b0 100644
--- a/examples/qdrant-example.ts
+++ b/examples/qdrant-example.ts
@@ -1,6 +1,6 @@
-import { CacheEngine, InMemoryAdapter, OpenAIEmbedder } from '@llm-cache/core';
-import { QdrantAdapter } from '@llm-cache/adapters-qdrant';
-import type { CacheConfig } from '@llm-cache/core';
+import { CacheEngine, InMemoryAdapter, OpenAIEmbedder } from '@reaatech/llm-cache';
+import { QdrantAdapter } from '@reaatech/llm-cache-adapters-qdrant';
+import type { CacheConfig } from '@reaatech/llm-cache';
const config: CacheConfig = {
storage: { adapter: 'memory' },
diff --git a/examples/redis-example.ts b/examples/redis-example.ts
index a64a8ab..a6eeb81 100644
--- a/examples/redis-example.ts
+++ b/examples/redis-example.ts
@@ -1,6 +1,6 @@
-import { CacheEngine, InMemoryAdapter, OpenAIEmbedder } from '@llm-cache/core';
-import { RedisAdapter } from '@llm-cache/adapters-redis';
-import type { CacheConfig } from '@llm-cache/core';
+import { CacheEngine, InMemoryAdapter, OpenAIEmbedder } from '@reaatech/llm-cache';
+import { RedisAdapter } from '@reaatech/llm-cache-adapters-redis';
+import type { CacheConfig } from '@reaatech/llm-cache';
const config: CacheConfig = {
storage: { adapter: 'redis' },
diff --git a/package.json b/package.json
index 2f1ad69..e49ed8d 100644
--- a/package.json
+++ b/package.json
@@ -16,40 +16,29 @@
"type": "module",
"engines": {
"node": ">=20.0.0",
- "pnpm": ">=8.0.0"
+ "pnpm": ">=10.0.0"
},
- "packageManager": "pnpm@8.15.0",
+ "packageManager": "pnpm@10.22.0",
"scripts": {
- "build": "pnpm --filter @llm-cache/core build && pnpm --filter @llm-cache/observability --filter @llm-cache/cost-tracker --filter @llm-cache/adapters-redis --filter @llm-cache/adapters-dynamodb --filter @llm-cache/adapters-qdrant build && pnpm --filter @llm-cache/server build",
+ "build": "pnpm -r build",
"test": "pnpm -r test",
"test:coverage": "pnpm -r test:coverage",
- "lint": "eslint . --ext .ts",
- "lint:fix": "eslint . --ext .ts --fix",
- "typecheck": "pnpm -r typecheck",
- "format": "prettier --write \"**/*.{ts,tsx,md,json}\"",
- "format:check": "prettier --check \"**/*.{ts,tsx,md,json}\""
+ "lint": "biome check .",
+ "lint:fix": "biome check --write .",
+ "format": "biome format --write .",
+ "typecheck": "tsc --noEmit -p tsconfig.typecheck.json",
+ "changeset": "changeset",
+ "version-packages": "changeset version",
+ "release": "pnpm build && changeset publish"
},
"devDependencies": {
+ "@biomejs/biome": "^1.9.4",
+ "@changesets/changelog-github": "^0.6.0",
+ "@changesets/cli": "^2.28.1",
"@types/node": "^20.11.0",
- "@typescript-eslint/eslint-plugin": "^7.0.0",
- "@typescript-eslint/parser": "^7.0.0",
- "@vitest/coverage-v8": "^1.3.0",
- "eslint": "^8.57.0",
- "eslint-config-prettier": "^9.1.0",
- "husky": "^9.0.0",
- "lint-staged": "^15.2.0",
- "prettier": "^3.2.0",
- "typescript": "^5.4.0",
- "vitest": "^1.3.0"
- },
- "lint-staged": {
- "*.{ts,tsx}": [
- "eslint --fix",
- "prettier --write"
- ],
- "*.{md,json,yaml,yml}": [
- "prettier --write"
- ]
+ "@vitest/coverage-v8": "3.2.4",
+ "typescript": "^5.8.3",
+ "vitest": "^3.1.1"
},
"pnpm": {
"overrides": {
diff --git a/packages/adapters/dynamodb/LICENSE b/packages/adapters/dynamodb/LICENSE
new file mode 100644
index 0000000..1390d41
--- /dev/null
+++ b/packages/adapters/dynamodb/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 llm-cache contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/adapters/dynamodb/package.json b/packages/adapters/dynamodb/package.json
index e514a63..c868896 100644
--- a/packages/adapters/dynamodb/package.json
+++ b/packages/adapters/dynamodb/package.json
@@ -1,5 +1,5 @@
{
- "name": "@llm-cache/adapters-dynamodb",
+ "name": "@reaatech/llm-cache-adapters-dynamodb",
"version": "0.1.0",
"author": "Rick Somers (https://reaatech.com)",
"description": "DynamoDB storage adapter for llm-cache",
@@ -7,7 +7,8 @@
"engines": {
"node": ">=20.0.0"
},
- "main": "./dist/index.js",
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.js",
"types": "./dist/index.d.ts",
"license": "MIT",
"repository": {
@@ -15,7 +16,7 @@
"url": "https://github.com/reaatech/llm-cache.git",
"directory": "packages/adapters/dynamodb"
},
- "homepage": "https://github.com/reaatech/llm-cache#readme",
+ "homepage": "https://github.com/reaatech/llm-cache/tree/main/packages/adapters/dynamodb#readme",
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
@@ -29,25 +30,27 @@
},
"exports": {
".": {
+ "types": "./dist/index.d.ts",
"import": "./dist/index.js",
- "types": "./dist/index.d.ts"
+ "require": "./dist/index.cjs"
}
},
"scripts": {
- "build": "tsc --build",
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"typecheck": "tsc --noEmit"
},
"dependencies": {
- "@llm-cache/core": "workspace:*",
+ "@reaatech/llm-cache": "workspace:*",
"@aws-sdk/client-dynamodb": "^3.500.0",
"@aws-sdk/lib-dynamodb": "^3.500.0"
},
"devDependencies": {
"@types/node": "^20.11.0",
- "vitest": "^1.3.0",
- "@vitest/coverage-v8": "^1.3.0"
+ "tsup": "^8.4.0",
+ "vitest": "^3.1.1",
+ "@vitest/coverage-v8": "3.2.4"
}
}
diff --git a/packages/adapters/dynamodb/src/DynamoDBAdapter.test.ts b/packages/adapters/dynamodb/src/DynamoDBAdapter.test.ts
index 17fa795..2fcc24f 100644
--- a/packages/adapters/dynamodb/src/DynamoDBAdapter.test.ts
+++ b/packages/adapters/dynamodb/src/DynamoDBAdapter.test.ts
@@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { DynamoDBAdapter } from './DynamoDBAdapter.js';
-import type { CacheEntry } from '@llm-cache/core';
+import type { CacheEntry } from '@reaatech/llm-cache';
function makeEntry(overrides?: Partial): CacheEntry {
const now = new Date();
diff --git a/packages/adapters/dynamodb/src/DynamoDBAdapter.ts b/packages/adapters/dynamodb/src/DynamoDBAdapter.ts
index 6b9fa36..a2ef582 100644
--- a/packages/adapters/dynamodb/src/DynamoDBAdapter.ts
+++ b/packages/adapters/dynamodb/src/DynamoDBAdapter.ts
@@ -9,8 +9,8 @@ import {
QueryCommand,
ScanCommand,
} from '@aws-sdk/lib-dynamodb';
-import type { CacheEntry, InvalidationCriteria, StorageStats, HealthStatus } from '@llm-cache/core';
-import type { StorageAdapter } from '@llm-cache/core';
+import type { CacheEntry, InvalidationCriteria, StorageStats, HealthStatus } from '@reaatech/llm-cache';
+import type { StorageAdapter } from '@reaatech/llm-cache';
export interface DynamoDBAdapterConfig {
region: string;
diff --git a/packages/adapters/dynamodb/tsconfig.json b/packages/adapters/dynamodb/tsconfig.json
index aba118c..c8c92cb 100644
--- a/packages/adapters/dynamodb/tsconfig.json
+++ b/packages/adapters/dynamodb/tsconfig.json
@@ -2,10 +2,7 @@
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
- "rootDir": "./src",
- "composite": true
+ "rootDir": "./src"
},
- "include": ["src/**/*"],
- "exclude": ["dist", "node_modules", "**/*.test.ts"],
- "references": [{ "path": "../../core" }]
+ "include": ["src/**/*"]
}
diff --git a/packages/adapters/qdrant/LICENSE b/packages/adapters/qdrant/LICENSE
new file mode 100644
index 0000000..1390d41
--- /dev/null
+++ b/packages/adapters/qdrant/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 llm-cache contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/adapters/qdrant/package.json b/packages/adapters/qdrant/package.json
index 8db8290..5c111c8 100644
--- a/packages/adapters/qdrant/package.json
+++ b/packages/adapters/qdrant/package.json
@@ -1,5 +1,5 @@
{
- "name": "@llm-cache/adapters-qdrant",
+ "name": "@reaatech/llm-cache-adapters-qdrant",
"version": "0.1.0",
"author": "Rick Somers (https://reaatech.com)",
"description": "Qdrant vector database adapter for llm-cache semantic search",
@@ -7,7 +7,8 @@
"engines": {
"node": ">=20.0.0"
},
- "main": "./dist/index.js",
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.js",
"types": "./dist/index.d.ts",
"license": "MIT",
"repository": {
@@ -15,7 +16,7 @@
"url": "https://github.com/reaatech/llm-cache.git",
"directory": "packages/adapters/qdrant"
},
- "homepage": "https://github.com/reaatech/llm-cache#readme",
+ "homepage": "https://github.com/reaatech/llm-cache/tree/main/packages/adapters/qdrant#readme",
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
@@ -29,26 +30,28 @@
},
"exports": {
".": {
+ "types": "./dist/index.d.ts",
"import": "./dist/index.js",
- "types": "./dist/index.d.ts"
+ "require": "./dist/index.cjs"
}
},
"scripts": {
- "build": "tsc --build",
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"typecheck": "tsc --noEmit"
},
"dependencies": {
- "@llm-cache/core": "workspace:*",
+ "@reaatech/llm-cache": "workspace:*",
"@qdrant/js-client-rest": "^1.8.0",
"uuid": "^9.0.0"
},
"devDependencies": {
"@types/node": "^20.11.0",
"@types/uuid": "^9.0.0",
- "vitest": "^1.3.0",
- "@vitest/coverage-v8": "^1.3.0"
+ "tsup": "^8.4.0",
+ "vitest": "^3.1.1",
+ "@vitest/coverage-v8": "3.2.4"
}
}
diff --git a/packages/adapters/qdrant/src/QdrantAdapter.test.ts b/packages/adapters/qdrant/src/QdrantAdapter.test.ts
index c7bd6f3..1f30bd4 100644
--- a/packages/adapters/qdrant/src/QdrantAdapter.test.ts
+++ b/packages/adapters/qdrant/src/QdrantAdapter.test.ts
@@ -1,7 +1,7 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { v5 as uuidv5 } from 'uuid';
import { QdrantAdapter } from './QdrantAdapter.js';
-import type { CacheEntry } from '@llm-cache/core';
+import type { CacheEntry } from '@reaatech/llm-cache';
const KEY_NAMESPACE = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
const pointId = (key: string) => uuidv5(key, KEY_NAMESPACE);
diff --git a/packages/adapters/qdrant/src/QdrantAdapter.ts b/packages/adapters/qdrant/src/QdrantAdapter.ts
index 0ddb27a..852f999 100644
--- a/packages/adapters/qdrant/src/QdrantAdapter.ts
+++ b/packages/adapters/qdrant/src/QdrantAdapter.ts
@@ -7,8 +7,8 @@ import type {
HealthStatus,
SimilarityResult,
VectorSearchFilters,
-} from '@llm-cache/core';
-import type { VectorStorageAdapter } from '@llm-cache/core';
+} from '@reaatech/llm-cache';
+import type { VectorStorageAdapter } from '@reaatech/llm-cache';
// Stable namespace UUID so the same key always maps to the same point ID across processes.
const KEY_NAMESPACE = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
diff --git a/packages/adapters/qdrant/tsconfig.json b/packages/adapters/qdrant/tsconfig.json
index aba118c..c8c92cb 100644
--- a/packages/adapters/qdrant/tsconfig.json
+++ b/packages/adapters/qdrant/tsconfig.json
@@ -2,10 +2,7 @@
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
- "rootDir": "./src",
- "composite": true
+ "rootDir": "./src"
},
- "include": ["src/**/*"],
- "exclude": ["dist", "node_modules", "**/*.test.ts"],
- "references": [{ "path": "../../core" }]
+ "include": ["src/**/*"]
}
diff --git a/packages/adapters/redis/LICENSE b/packages/adapters/redis/LICENSE
new file mode 100644
index 0000000..1390d41
--- /dev/null
+++ b/packages/adapters/redis/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 llm-cache contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/adapters/redis/package.json b/packages/adapters/redis/package.json
index 337f175..f583261 100644
--- a/packages/adapters/redis/package.json
+++ b/packages/adapters/redis/package.json
@@ -1,5 +1,5 @@
{
- "name": "@llm-cache/adapters-redis",
+ "name": "@reaatech/llm-cache-adapters-redis",
"version": "0.1.0",
"author": "Rick Somers (https://reaatech.com)",
"description": "Redis storage adapter for llm-cache",
@@ -7,7 +7,8 @@
"engines": {
"node": ">=20.0.0"
},
- "main": "./dist/index.js",
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.js",
"types": "./dist/index.d.ts",
"license": "MIT",
"repository": {
@@ -15,7 +16,7 @@
"url": "https://github.com/reaatech/llm-cache.git",
"directory": "packages/adapters/redis"
},
- "homepage": "https://github.com/reaatech/llm-cache#readme",
+ "homepage": "https://github.com/reaatech/llm-cache/tree/main/packages/adapters/redis#readme",
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
@@ -29,24 +30,26 @@
},
"exports": {
".": {
+ "types": "./dist/index.d.ts",
"import": "./dist/index.js",
- "types": "./dist/index.d.ts"
+ "require": "./dist/index.cjs"
}
},
"scripts": {
- "build": "tsc --build",
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"typecheck": "tsc --noEmit"
},
"dependencies": {
- "@llm-cache/core": "workspace:*",
+ "@reaatech/llm-cache": "workspace:*",
"redis": "^4.6.0"
},
"devDependencies": {
"@types/node": "^20.11.0",
- "vitest": "^1.3.0",
- "@vitest/coverage-v8": "^1.3.0"
+ "tsup": "^8.4.0",
+ "vitest": "^3.1.1",
+ "@vitest/coverage-v8": "3.2.4"
}
}
diff --git a/packages/adapters/redis/src/RedisAdapter.test.ts b/packages/adapters/redis/src/RedisAdapter.test.ts
index d3ec320..e8e59bc 100644
--- a/packages/adapters/redis/src/RedisAdapter.test.ts
+++ b/packages/adapters/redis/src/RedisAdapter.test.ts
@@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { RedisAdapter } from './RedisAdapter.js';
-import type { CacheEntry } from '@llm-cache/core';
+import type { CacheEntry } from '@reaatech/llm-cache';
function makeEntry(overrides?: Partial): CacheEntry {
const now = new Date();
diff --git a/packages/adapters/redis/src/RedisAdapter.ts b/packages/adapters/redis/src/RedisAdapter.ts
index c69b6d9..03ac26e 100644
--- a/packages/adapters/redis/src/RedisAdapter.ts
+++ b/packages/adapters/redis/src/RedisAdapter.ts
@@ -1,6 +1,6 @@
import { createClient, type RedisClientType } from 'redis';
-import type { CacheEntry, InvalidationCriteria, StorageStats, HealthStatus } from '@llm-cache/core';
-import type { StorageAdapter } from '@llm-cache/core';
+import type { CacheEntry, InvalidationCriteria, StorageStats, HealthStatus } from '@reaatech/llm-cache';
+import type { StorageAdapter } from '@reaatech/llm-cache';
export interface RedisAdapterConfig {
url: string;
diff --git a/packages/adapters/redis/tsconfig.json b/packages/adapters/redis/tsconfig.json
index aba118c..c8c92cb 100644
--- a/packages/adapters/redis/tsconfig.json
+++ b/packages/adapters/redis/tsconfig.json
@@ -2,10 +2,7 @@
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
- "rootDir": "./src",
- "composite": true
+ "rootDir": "./src"
},
- "include": ["src/**/*"],
- "exclude": ["dist", "node_modules", "**/*.test.ts"],
- "references": [{ "path": "../../core" }]
+ "include": ["src/**/*"]
}
diff --git a/packages/core/LICENSE b/packages/core/LICENSE
new file mode 100644
index 0000000..1390d41
--- /dev/null
+++ b/packages/core/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 llm-cache contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/core/package.json b/packages/core/package.json
index 08ca96c..658c958 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,5 +1,5 @@
{
- "name": "@llm-cache/core",
+ "name": "@reaatech/llm-cache",
"version": "0.1.0",
"author": "Rick Somers (https://reaatech.com)",
"description": "Core caching engine for llm-cache — semantic and exact-match caching with embedding-based similarity",
@@ -7,7 +7,8 @@
"engines": {
"node": ">=20.0.0"
},
- "main": "./dist/index.js",
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.js",
"types": "./dist/index.d.ts",
"license": "MIT",
"repository": {
@@ -15,7 +16,7 @@
"url": "https://github.com/reaatech/llm-cache.git",
"directory": "packages/core"
},
- "homepage": "https://github.com/reaatech/llm-cache#readme",
+ "homepage": "https://github.com/reaatech/llm-cache/tree/main/packages/core#readme",
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
@@ -36,32 +37,13 @@
},
"exports": {
".": {
+ "types": "./dist/index.d.ts",
"import": "./dist/index.js",
- "types": "./dist/index.d.ts"
- },
- "./cache": {
- "import": "./dist/cache/index.js",
- "types": "./dist/cache/index.d.ts"
- },
- "./embedding": {
- "import": "./dist/embedding/index.js",
- "types": "./dist/embedding/index.d.ts"
- },
- "./storage": {
- "import": "./dist/storage/index.js",
- "types": "./dist/storage/index.d.ts"
- },
- "./config": {
- "import": "./dist/config/index.js",
- "types": "./dist/config/index.d.ts"
- },
- "./types": {
- "import": "./dist/types/index.js",
- "types": "./dist/types/index.d.ts"
+ "require": "./dist/index.cjs"
}
},
"scripts": {
- "build": "tsc --build",
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
@@ -74,7 +56,8 @@
"devDependencies": {
"@types/node": "^20.11.0",
"@types/uuid": "^9.0.0",
- "vitest": "^1.3.0",
- "@vitest/coverage-v8": "^1.3.0"
+ "tsup": "^8.4.0",
+ "vitest": "^3.1.1",
+ "@vitest/coverage-v8": "3.2.4"
}
}
diff --git a/packages/core/src/cache/CacheEngine.integration.test.ts b/packages/core/src/cache/CacheEngine.integration.test.ts
index f1c4897..f583a62 100644
--- a/packages/core/src/cache/CacheEngine.integration.test.ts
+++ b/packages/core/src/cache/CacheEngine.integration.test.ts
@@ -1,6 +1,6 @@
import { describe, it, expect, beforeEach } from 'vitest';
-import { CacheEngine, InMemoryAdapter } from '@llm-cache/core';
-import type { CacheConfig, EmbeddingProvider } from '@llm-cache/core';
+import { CacheEngine, InMemoryAdapter } from '@reaatech/llm-cache';
+import type { CacheConfig, EmbeddingProvider } from '@reaatech/llm-cache';
class DeterministicEmbedder implements EmbeddingProvider {
embed(text: string): Promise {
diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json
index 53dc78f..90d76d7 100644
--- a/packages/core/tsconfig.json
+++ b/packages/core/tsconfig.json
@@ -2,9 +2,7 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
- "rootDir": "./src",
- "composite": true
+ "rootDir": "./src"
},
- "include": ["src/**/*"],
- "exclude": ["dist", "node_modules", "**/*.test.ts"]
+ "include": ["src/**/*"]
}
diff --git a/packages/cost-tracker/LICENSE b/packages/cost-tracker/LICENSE
new file mode 100644
index 0000000..1390d41
--- /dev/null
+++ b/packages/cost-tracker/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 llm-cache contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/cost-tracker/package.json b/packages/cost-tracker/package.json
index 779093a..9e20b65 100644
--- a/packages/cost-tracker/package.json
+++ b/packages/cost-tracker/package.json
@@ -1,5 +1,5 @@
{
- "name": "@llm-cache/cost-tracker",
+ "name": "@reaatech/llm-cache-cost-tracker",
"version": "0.1.0",
"author": "Rick Somers (https://reaatech.com)",
"description": "Cost tracking and pricing calculations for llm-cache",
@@ -7,7 +7,8 @@
"engines": {
"node": ">=20.0.0"
},
- "main": "./dist/index.js",
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.js",
"types": "./dist/index.d.ts",
"license": "MIT",
"repository": {
@@ -15,7 +16,7 @@
"url": "https://github.com/reaatech/llm-cache.git",
"directory": "packages/cost-tracker"
},
- "homepage": "https://github.com/reaatech/llm-cache#readme",
+ "homepage": "https://github.com/reaatech/llm-cache/tree/main/packages/cost-tracker#readme",
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
@@ -29,23 +30,25 @@
},
"exports": {
".": {
+ "types": "./dist/index.d.ts",
"import": "./dist/index.js",
- "types": "./dist/index.d.ts"
+ "require": "./dist/index.cjs"
}
},
"scripts": {
- "build": "tsc --build",
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"typecheck": "tsc --noEmit"
},
"dependencies": {
- "@llm-cache/core": "workspace:*"
+ "@reaatech/llm-cache": "workspace:*"
},
"devDependencies": {
"@types/node": "^20.11.0",
- "vitest": "^1.3.0",
- "@vitest/coverage-v8": "^1.3.0"
+ "tsup": "^8.4.0",
+ "vitest": "^3.1.1",
+ "@vitest/coverage-v8": "3.2.4"
}
}
diff --git a/packages/cost-tracker/tsconfig.json b/packages/cost-tracker/tsconfig.json
index 49a9966..90d76d7 100644
--- a/packages/cost-tracker/tsconfig.json
+++ b/packages/cost-tracker/tsconfig.json
@@ -2,10 +2,7 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
- "rootDir": "./src",
- "composite": true
+ "rootDir": "./src"
},
- "include": ["src/**/*"],
- "exclude": ["dist", "node_modules", "**/*.test.ts"],
- "references": [{ "path": "../core" }]
+ "include": ["src/**/*"]
}
diff --git a/packages/observability/LICENSE b/packages/observability/LICENSE
new file mode 100644
index 0000000..1390d41
--- /dev/null
+++ b/packages/observability/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 llm-cache contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/observability/package.json b/packages/observability/package.json
index 2afc670..0344433 100644
--- a/packages/observability/package.json
+++ b/packages/observability/package.json
@@ -1,5 +1,5 @@
{
- "name": "@llm-cache/observability",
+ "name": "@reaatech/llm-cache-observability",
"version": "0.1.0",
"author": "Rick Somers (https://reaatech.com)",
"description": "Observability, metrics, logging, and tracing for llm-cache",
@@ -7,7 +7,8 @@
"engines": {
"node": ">=20.0.0"
},
- "main": "./dist/index.js",
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.js",
"types": "./dist/index.d.ts",
"license": "MIT",
"repository": {
@@ -15,7 +16,7 @@
"url": "https://github.com/reaatech/llm-cache.git",
"directory": "packages/observability"
},
- "homepage": "https://github.com/reaatech/llm-cache#readme",
+ "homepage": "https://github.com/reaatech/llm-cache/tree/main/packages/observability#readme",
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
@@ -29,12 +30,13 @@
},
"exports": {
".": {
+ "types": "./dist/index.d.ts",
"import": "./dist/index.js",
- "types": "./dist/index.d.ts"
+ "require": "./dist/index.cjs"
}
},
"scripts": {
- "build": "tsc --build",
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
@@ -43,7 +45,8 @@
"dependencies": {},
"devDependencies": {
"@types/node": "^20.11.0",
- "vitest": "^1.3.0",
- "@vitest/coverage-v8": "^1.3.0"
+ "tsup": "^8.4.0",
+ "vitest": "^3.1.1",
+ "@vitest/coverage-v8": "3.2.4"
}
}
diff --git a/packages/observability/tsconfig.json b/packages/observability/tsconfig.json
index 53dc78f..90d76d7 100644
--- a/packages/observability/tsconfig.json
+++ b/packages/observability/tsconfig.json
@@ -2,9 +2,7 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
- "rootDir": "./src",
- "composite": true
+ "rootDir": "./src"
},
- "include": ["src/**/*"],
- "exclude": ["dist", "node_modules", "**/*.test.ts"]
+ "include": ["src/**/*"]
}
diff --git a/packages/server/Dockerfile b/packages/server/Dockerfile
index 5539221..2c41664 100644
--- a/packages/server/Dockerfile
+++ b/packages/server/Dockerfile
@@ -20,7 +20,7 @@ FROM dependencies AS build
COPY packages ./packages
COPY tsconfig.json .
-RUN pnpm build --filter=@llm-cache/server...
+RUN pnpm build --filter=@reaatech/llm-cache-server...
FROM node:20-alpine AS production
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
diff --git a/packages/server/LICENSE b/packages/server/LICENSE
new file mode 100644
index 0000000..1390d41
--- /dev/null
+++ b/packages/server/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 llm-cache contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/server/package.json b/packages/server/package.json
index ea969b6..6b54694 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -1,5 +1,5 @@
{
- "name": "@llm-cache/server",
+ "name": "@reaatech/llm-cache-server",
"version": "0.1.0",
"author": "Rick Somers (https://reaatech.com)",
"description": "HTTP service wrapper for llm-cache",
@@ -7,7 +7,8 @@
"engines": {
"node": ">=20.0.0"
},
- "main": "./dist/index.js",
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.js",
"types": "./dist/index.d.ts",
"license": "MIT",
"repository": {
@@ -15,7 +16,7 @@
"url": "https://github.com/reaatech/llm-cache.git",
"directory": "packages/server"
},
- "homepage": "https://github.com/reaatech/llm-cache#readme",
+ "homepage": "https://github.com/reaatech/llm-cache/tree/main/packages/server#readme",
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
@@ -32,12 +33,13 @@
},
"exports": {
".": {
+ "types": "./dist/index.d.ts",
"import": "./dist/index.js",
- "types": "./dist/index.d.ts"
+ "require": "./dist/index.cjs"
}
},
"scripts": {
- "build": "tsc --build",
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
@@ -45,16 +47,17 @@
"start": "node dist/index.js"
},
"dependencies": {
- "@llm-cache/core": "workspace:*",
- "@llm-cache/adapters-redis": "workspace:*",
- "@llm-cache/adapters-dynamodb": "workspace:*",
- "@llm-cache/adapters-qdrant": "workspace:*",
- "@llm-cache/cost-tracker": "workspace:*",
- "@llm-cache/observability": "workspace:*"
+ "@reaatech/llm-cache": "workspace:*",
+ "@reaatech/llm-cache-adapters-redis": "workspace:*",
+ "@reaatech/llm-cache-adapters-dynamodb": "workspace:*",
+ "@reaatech/llm-cache-adapters-qdrant": "workspace:*",
+ "@reaatech/llm-cache-cost-tracker": "workspace:*",
+ "@reaatech/llm-cache-observability": "workspace:*"
},
"devDependencies": {
"@types/node": "^20.11.0",
- "vitest": "^1.3.0",
- "@vitest/coverage-v8": "^1.3.0"
+ "tsup": "^8.4.0",
+ "vitest": "^3.1.1",
+ "@vitest/coverage-v8": "3.2.4"
}
}
diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts
index e69b2f7..d8a1104 100644
--- a/packages/server/src/app.ts
+++ b/packages/server/src/app.ts
@@ -7,11 +7,11 @@ import {
OpenAIEmbedder,
type CacheOptions,
type EmbeddingProvider,
-} from '@llm-cache/core';
-import { RedisAdapter } from '@llm-cache/adapters-redis';
-import { DynamoDBAdapter } from '@llm-cache/adapters-dynamodb';
-import { QdrantAdapter } from '@llm-cache/adapters-qdrant';
-import { MetricsCollector, Logger } from '@llm-cache/observability';
+} from '@reaatech/llm-cache';
+import { RedisAdapter } from '@reaatech/llm-cache-adapters-redis';
+import { DynamoDBAdapter } from '@reaatech/llm-cache-adapters-dynamodb';
+import { QdrantAdapter } from '@reaatech/llm-cache-adapters-qdrant';
+import { MetricsCollector, Logger } from '@reaatech/llm-cache-observability';
import { loadConfig } from './config.js';
const config = loadConfig();
diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts
index 32a0aef..16a5499 100644
--- a/packages/server/src/config.ts
+++ b/packages/server/src/config.ts
@@ -1,4 +1,4 @@
-import { CacheConfigSchema, type CacheConfig } from '@llm-cache/core';
+import { CacheConfigSchema, type CacheConfig } from '@reaatech/llm-cache';
export interface ServerConfig {
port: number;
diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json
index b6222a0..90d76d7 100644
--- a/packages/server/tsconfig.json
+++ b/packages/server/tsconfig.json
@@ -2,17 +2,7 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
- "rootDir": "./src",
- "composite": true
+ "rootDir": "./src"
},
- "include": ["src/**/*"],
- "exclude": ["dist", "node_modules", "**/*.test.ts"],
- "references": [
- { "path": "../core" },
- { "path": "../adapters/redis" },
- { "path": "../adapters/dynamodb" },
- { "path": "../adapters/qdrant" },
- { "path": "../cost-tracker" },
- { "path": "../observability" }
- ]
+ "include": ["src/**/*"]
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 82b8786..2267166 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -48,16 +48,16 @@ importers:
examples:
dependencies:
- '@llm-cache/adapters-dynamodb':
+ '@reaatech/llm-cache-adapters-dynamodb':
specifier: workspace:*
version: link:../packages/adapters/dynamodb
- '@llm-cache/adapters-qdrant':
+ '@reaatech/llm-cache-adapters-qdrant':
specifier: workspace:*
version: link:../packages/adapters/qdrant
- '@llm-cache/adapters-redis':
+ '@reaatech/llm-cache-adapters-redis':
specifier: workspace:*
version: link:../packages/adapters/redis
- '@llm-cache/core':
+ '@reaatech/llm-cache':
specifier: workspace:*
version: link:../packages/core
devDependencies:
@@ -76,7 +76,7 @@ importers:
'@aws-sdk/lib-dynamodb':
specifier: ^3.500.0
version: 3.1035.0(@aws-sdk/client-dynamodb@3.1035.0)
- '@llm-cache/core':
+ '@reaatech/llm-cache':
specifier: workspace:*
version: link:../../core
devDependencies:
@@ -92,7 +92,7 @@ importers:
packages/adapters/qdrant:
dependencies:
- '@llm-cache/core':
+ '@reaatech/llm-cache':
specifier: workspace:*
version: link:../../core
'@qdrant/js-client-rest':
@@ -117,7 +117,7 @@ importers:
packages/adapters/redis:
dependencies:
- '@llm-cache/core':
+ '@reaatech/llm-cache':
specifier: workspace:*
version: link:../../core
redis:
@@ -158,7 +158,7 @@ importers:
packages/cost-tracker:
dependencies:
- '@llm-cache/core':
+ '@reaatech/llm-cache':
specifier: workspace:*
version: link:../core
devDependencies:
@@ -186,22 +186,22 @@ importers:
packages/server:
dependencies:
- '@llm-cache/adapters-dynamodb':
+ '@reaatech/llm-cache-adapters-dynamodb':
specifier: workspace:*
version: link:../adapters/dynamodb
- '@llm-cache/adapters-qdrant':
+ '@reaatech/llm-cache-adapters-qdrant':
specifier: workspace:*
version: link:../adapters/qdrant
- '@llm-cache/adapters-redis':
+ '@reaatech/llm-cache-adapters-redis':
specifier: workspace:*
version: link:../adapters/redis
- '@llm-cache/core':
+ '@reaatech/llm-cache':
specifier: workspace:*
version: link:../core
- '@llm-cache/cost-tracker':
+ '@reaatech/llm-cache-cost-tracker':
specifier: workspace:*
version: link:../cost-tracker
- '@llm-cache/observability':
+ '@reaatech/llm-cache-observability':
specifier: workspace:*
version: link:../observability
devDependencies:
diff --git a/tsconfig.json b/tsconfig.json
index 5c0f1b8..bbd6158 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -26,5 +26,5 @@
"isolatedModules": true,
"verbatimModuleSyntax": true
},
- "exclude": ["node_modules", "dist", "coverage"]
+ "exclude": ["node_modules", "dist", "*.config.js"]
}
diff --git a/tsconfig.typecheck.json b/tsconfig.typecheck.json
new file mode 100644
index 0000000..c580960
--- /dev/null
+++ b/tsconfig.typecheck.json
@@ -0,0 +1,15 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@reaatech/llm-cache": ["./packages/core/src/index.ts"],
+ "@reaatech/llm-cache-server": ["./packages/server/src/index.ts"],
+ "@reaatech/llm-cache-adapters-redis": ["./packages/adapters/redis/src/index.ts"],
+ "@reaatech/llm-cache-adapters-dynamodb": ["./packages/adapters/dynamodb/src/index.ts"],
+ "@reaatech/llm-cache-adapters-qdrant": ["./packages/adapters/qdrant/src/index.ts"],
+ "@reaatech/llm-cache-cost-tracker": ["./packages/cost-tracker/src/index.ts"],
+ "@reaatech/llm-cache-observability": ["./packages/observability/src/index.ts"]
+ }
+ }
+}
From f09564e0bcf68e12755cc4cb4548c4c46810001f Mon Sep 17 00:00:00 2001
From: reaatech <138725666+reaatech@users.noreply.github.com>
Date: Thu, 30 Apr 2026 05:47:56 -0700
Subject: [PATCH 03/11] fix(ci): remove --frozen-lockfile to allow pnpm to
regenerate stale lockfile
The lockfile was manually updated during the package rename and its
internal hash no longer matches the current configuration. Remove
--frozen-lockfile so pnpm 10 can auto-migrate and fix the lockfile.
---
.github/workflows/ci.yml | 12 ++++++------
.github/workflows/release.yml | 2 +-
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9bacf1b..ec01e57 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -27,7 +27,7 @@ jobs:
cache: 'pnpm'
- name: Install dependencies
- run: pnpm install --frozen-lockfile
+ run: pnpm install
- name: Lint
run: pnpm lint
@@ -52,7 +52,7 @@ jobs:
cache: 'pnpm'
- name: Install dependencies
- run: pnpm install --frozen-lockfile
+ run: pnpm install
- name: Dependency audit
run: pnpm audit --audit-level=high
@@ -87,7 +87,7 @@ jobs:
cache: 'pnpm'
- name: Install dependencies
- run: pnpm install --frozen-lockfile
+ run: pnpm install
- name: Test ${{ matrix.package }}
run: pnpm --filter ${{ matrix.package }} test
@@ -110,7 +110,7 @@ jobs:
cache: 'pnpm'
- name: Install dependencies
- run: pnpm install --frozen-lockfile
+ run: pnpm install
- name: Run coverage (core)
run: pnpm --filter @reaatech/llm-cache test:coverage
@@ -139,7 +139,7 @@ jobs:
cache: 'pnpm'
- name: Install dependencies
- run: pnpm install --frozen-lockfile
+ run: pnpm install
- name: Build all packages
run: pnpm build
@@ -188,7 +188,7 @@ jobs:
cache: 'pnpm'
- name: Install dependencies
- run: pnpm install --frozen-lockfile
+ run: pnpm install
- name: Build all packages
run: pnpm build
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 95a498a..a44c2da 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -39,7 +39,7 @@ jobs:
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
- run: pnpm install --frozen-lockfile
+ run: pnpm install
- name: Build packages
run: pnpm build
From 7181231f14a8a061bfaee5bd999d236ead713d9b Mon Sep 17 00:00:00 2001
From: reaatech <138725666+reaatech@users.noreply.github.com>
Date: Thu, 30 Apr 2026 05:52:42 -0700
Subject: [PATCH 04/11] fix: apply biome formatting and resolve lint errors
- Auto-formatted 47 files with biome (JSON whitespace, import ordering, etc.)
- Fixed 8 noExplicitAny violations with biome-ignore comments in test mocks
- Fixed 1 noNonNullAssertion in QdrantAdapter.ts (hit.payload! -> hit.payload ?? {})
- Migrated biome.json from v1 to v2 schema format
---
.changeset/config.json | 5 +-
biome.json | 8 +--
examples/basic-usage.ts | 6 +-
examples/qdrant-example.ts | 4 +-
examples/redis-example.ts | 4 +-
packages/adapters/dynamodb/package.json | 8 ++-
.../dynamodb/src/DynamoDBAdapter.test.ts | 18 +++---
.../adapters/dynamodb/src/DynamoDBAdapter.ts | 63 +++++++++++--------
packages/adapters/qdrant/package.json | 8 ++-
.../adapters/qdrant/src/QdrantAdapter.test.ts | 14 +++--
packages/adapters/qdrant/src/QdrantAdapter.ts | 40 +++++++-----
packages/adapters/redis/package.json | 7 ++-
.../adapters/redis/src/RedisAdapter.test.ts | 10 +--
packages/adapters/redis/src/RedisAdapter.ts | 9 ++-
.../src/cache/CacheEngine.integration.test.ts | 18 +++---
packages/core/src/cache/CacheEngine.test.ts | 24 +++----
packages/core/src/cache/CacheEngine.ts | 46 +++++++-------
.../core/src/cache/SimilarityMatcher.test.ts | 6 +-
packages/core/src/cache/SimilarityMatcher.ts | 4 +-
packages/core/src/config/CacheConfig.test.ts | 8 +--
packages/core/src/config/index.ts | 2 +-
.../core/src/embedding/OpenAIEmbedder.test.ts | 6 +-
packages/core/src/embedding/OpenAIEmbedder.ts | 13 ++--
packages/core/src/embedding/index.ts | 2 +-
packages/core/src/index.test.ts | 2 +-
packages/core/src/index.ts | 14 ++---
.../core/src/storage/InMemoryAdapter.test.ts | 7 +--
packages/core/src/storage/InMemoryAdapter.ts | 10 +--
packages/core/src/storage/StorageAdapter.ts | 6 +-
packages/core/src/storage/index.ts | 2 +-
packages/core/src/types/index.ts | 2 +-
packages/core/src/utils/encryption.test.ts | 4 +-
packages/core/src/utils/encryption.ts | 4 +-
packages/core/src/utils/hash.test.ts | 4 +-
packages/core/src/utils/hash.ts | 2 +-
packages/core/src/utils/index.ts | 2 +-
packages/cost-tracker/package.json | 8 ++-
.../cost-tracker/src/CostCalculator.test.ts | 2 +-
packages/cost-tracker/src/CostCalculator.ts | 4 +-
packages/cost-tracker/src/index.ts | 2 +-
packages/observability/package.json | 8 ++-
packages/observability/src/index.ts | 2 +-
.../src/metrics/MetricsCollector.test.ts | 2 +-
.../src/metrics/MetricsCollector.ts | 2 +-
packages/server/package.json | 7 ++-
packages/server/src/app.test.ts | 6 +-
packages/server/src/app.ts | 27 ++++----
packages/server/src/config.ts | 8 +--
packages/server/src/index.test.ts | 2 +-
49 files changed, 255 insertions(+), 217 deletions(-)
diff --git a/.changeset/config.json b/.changeset/config.json
index 28fd043..c96f647 100644
--- a/.changeset/config.json
+++ b/.changeset/config.json
@@ -1,9 +1,6 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json",
- "changelog": [
- "@changesets/changelog-github",
- { "repo": "reaatech/llm-cache" }
- ],
+ "changelog": ["@changesets/changelog-github", { "repo": "reaatech/llm-cache" }],
"commit": false,
"fixed": [],
"linked": [],
diff --git a/biome.json b/biome.json
index da77c68..e48a70d 100644
--- a/biome.json
+++ b/biome.json
@@ -1,11 +1,9 @@
{
- "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
+ "$schema": "https://biomejs.dev/schemas/2.4.13/schema.json",
"files": {
- "ignore": ["dist", "node_modules", "coverage", ".turbo"]
- },
- "organizeImports": {
- "enabled": true
+ "includes": ["**", "!**/dist", "!**/node_modules", "!**/coverage", "!**/.turbo"]
},
+ "assist": { "actions": { "source": { "organizeImports": "on" } } },
"linter": {
"enabled": true,
"rules": {
diff --git a/examples/basic-usage.ts b/examples/basic-usage.ts
index 21bbd56..bacab89 100644
--- a/examples/basic-usage.ts
+++ b/examples/basic-usage.ts
@@ -1,5 +1,5 @@
-import { CacheEngine, InMemoryAdapter, OpenAIEmbedder } from '@reaatech/llm-cache';
import type { CacheConfig } from '@reaatech/llm-cache';
+import { CacheEngine, InMemoryAdapter, OpenAIEmbedder } from '@reaatech/llm-cache';
const config: CacheConfig = {
storage: { adapter: 'memory' },
@@ -63,7 +63,7 @@ async function main() {
await cache.set(
'What is TypeScript?',
{ choices: [{ message: { content: 'TypeScript is a typed superset of JavaScript.' } }] },
- { model: 'gpt-4', modelVersion: 'gpt-4-0613' }
+ { model: 'gpt-4', modelVersion: 'gpt-4-0613' },
);
// Exact match
@@ -82,7 +82,7 @@ async function main() {
});
console.log(
'Semantic match:',
- semantic.hit ? `${semantic.type} hit (confidence: ${semantic.confidence})` : 'miss'
+ semantic.hit ? `${semantic.type} hit (confidence: ${semantic.confidence})` : 'miss',
);
// Miss
diff --git a/examples/qdrant-example.ts b/examples/qdrant-example.ts
index 012e0b0..64c27ff 100644
--- a/examples/qdrant-example.ts
+++ b/examples/qdrant-example.ts
@@ -1,6 +1,6 @@
+import type { CacheConfig } from '@reaatech/llm-cache';
import { CacheEngine, InMemoryAdapter, OpenAIEmbedder } from '@reaatech/llm-cache';
import { QdrantAdapter } from '@reaatech/llm-cache-adapters-qdrant';
-import type { CacheConfig } from '@reaatech/llm-cache';
const config: CacheConfig = {
storage: { adapter: 'memory' },
@@ -71,7 +71,7 @@ async function main() {
await cache.set(
'Explain quantum computing',
{ answer: 'Quantum computing uses qubits...' },
- { model: 'gpt-4', modelVersion: 'gpt-4-0613' }
+ { model: 'gpt-4', modelVersion: 'gpt-4-0613' },
);
const result = await cache.get('What is quantum computing?', {
diff --git a/examples/redis-example.ts b/examples/redis-example.ts
index a6eeb81..82dcb19 100644
--- a/examples/redis-example.ts
+++ b/examples/redis-example.ts
@@ -1,6 +1,6 @@
+import type { CacheConfig } from '@reaatech/llm-cache';
import { CacheEngine, InMemoryAdapter, OpenAIEmbedder } from '@reaatech/llm-cache';
import { RedisAdapter } from '@reaatech/llm-cache-adapters-redis';
-import type { CacheConfig } from '@reaatech/llm-cache';
const config: CacheConfig = {
storage: { adapter: 'redis' },
@@ -67,7 +67,7 @@ async function main() {
await cache.set(
'What is Redis?',
{ answer: 'An in-memory data structure store' },
- { model: 'gpt-4', modelVersion: 'gpt-4-0613' }
+ { model: 'gpt-4', modelVersion: 'gpt-4-0613' },
);
const result = await cache.get('What is Redis?', { model: 'gpt-4', modelVersion: 'gpt-4-0613' });
diff --git a/packages/adapters/dynamodb/package.json b/packages/adapters/dynamodb/package.json
index c868896..e944edb 100644
--- a/packages/adapters/dynamodb/package.json
+++ b/packages/adapters/dynamodb/package.json
@@ -20,7 +20,13 @@
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
- "keywords": ["llm-cache", "dynamodb", "aws", "cache", "adapter"],
+ "keywords": [
+ "llm-cache",
+ "dynamodb",
+ "aws",
+ "cache",
+ "adapter"
+ ],
"files": [
"dist",
"README.md"
diff --git a/packages/adapters/dynamodb/src/DynamoDBAdapter.test.ts b/packages/adapters/dynamodb/src/DynamoDBAdapter.test.ts
index 2fcc24f..c26f058 100644
--- a/packages/adapters/dynamodb/src/DynamoDBAdapter.test.ts
+++ b/packages/adapters/dynamodb/src/DynamoDBAdapter.test.ts
@@ -1,6 +1,6 @@
-import { describe, it, expect, vi, beforeEach } from 'vitest';
-import { DynamoDBAdapter } from './DynamoDBAdapter.js';
import type { CacheEntry } from '@reaatech/llm-cache';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+import { DynamoDBAdapter } from './DynamoDBAdapter.js';
function makeEntry(overrides?: Partial): CacheEntry {
const now = new Date();
@@ -63,7 +63,7 @@ describe('DynamoDBAdapter', () => {
mockSend = vi.fn().mockResolvedValue({});
adapter = new DynamoDBAdapter({ region: 'us-east-1', tableName: 'test' });
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ // biome-ignore lint/suspicious/noExplicitAny: test mock injection
(adapter as any).client = { send: mockSend } as any;
});
@@ -76,8 +76,8 @@ describe('DynamoDBAdapter', () => {
mockSend.mockResolvedValueOnce({ Item: itemFromEntry('key', entry) });
const result = await adapter.get('key');
expect(result).not.toBeNull();
- expect(result!.prompt).toBe('test');
- expect(result!.id).toBe('test-id');
+ expect(result?.prompt).toBe('test');
+ expect(result?.id).toBe('test-id');
});
it('should return null for missing key', async () => {
@@ -109,7 +109,7 @@ describe('DynamoDBAdapter', () => {
expect(command.input.Item.pk).toBe('key');
expect(command.input.Item.id).toBe(entry.id);
expect(command.input.Item.expiresAtEpoch).toBe(
- Math.floor(entry.metadata.expiresAt.getTime() / 1000)
+ Math.floor(entry.metadata.expiresAt.getTime() / 1000),
);
});
@@ -119,14 +119,12 @@ describe('DynamoDBAdapter', () => {
tableName: 'test',
ttlAttribute: 'ttl',
});
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ // biome-ignore lint/suspicious/noExplicitAny: test mock injection
(customAdapter as any).client = { send: mockSend } as any;
const entry = makeEntry();
await customAdapter.set('key', entry);
const command = mockSend.mock.calls[0][0];
- expect(command.input.Item.ttl).toBe(
- Math.floor(entry.metadata.expiresAt.getTime() / 1000)
- );
+ expect(command.input.Item.ttl).toBe(Math.floor(entry.metadata.expiresAt.getTime() / 1000));
});
it('should delete a key', async () => {
diff --git a/packages/adapters/dynamodb/src/DynamoDBAdapter.ts b/packages/adapters/dynamodb/src/DynamoDBAdapter.ts
index a2ef582..58d0f3d 100644
--- a/packages/adapters/dynamodb/src/DynamoDBAdapter.ts
+++ b/packages/adapters/dynamodb/src/DynamoDBAdapter.ts
@@ -1,16 +1,21 @@
-import { DynamoDBClient, DescribeTableCommand } from '@aws-sdk/client-dynamodb';
+import { DescribeTableCommand, DynamoDBClient } from '@aws-sdk/client-dynamodb';
import {
+ BatchGetCommand,
+ BatchWriteCommand,
+ DeleteCommand,
DynamoDBDocumentClient,
GetCommand,
PutCommand,
- DeleteCommand,
- BatchGetCommand,
- BatchWriteCommand,
QueryCommand,
ScanCommand,
} from '@aws-sdk/lib-dynamodb';
-import type { CacheEntry, InvalidationCriteria, StorageStats, HealthStatus } from '@reaatech/llm-cache';
-import type { StorageAdapter } from '@reaatech/llm-cache';
+import type {
+ CacheEntry,
+ HealthStatus,
+ InvalidationCriteria,
+ StorageAdapter,
+ StorageStats,
+} from '@reaatech/llm-cache';
export interface DynamoDBAdapterConfig {
region: string;
@@ -44,7 +49,7 @@ export class DynamoDBAdapter implements StorageAdapter {
new GetCommand({
TableName: this.tableName,
Key: { pk: key },
- })
+ }),
);
if (!result.Item) return null;
@@ -63,7 +68,7 @@ export class DynamoDBAdapter implements StorageAdapter {
new PutCommand({
TableName: this.tableName,
Item: this.serialize(key, entry),
- })
+ }),
);
}
@@ -72,7 +77,7 @@ export class DynamoDBAdapter implements StorageAdapter {
new DeleteCommand({
TableName: this.tableName,
Key: { pk: key },
- })
+ }),
);
return true;
}
@@ -92,7 +97,7 @@ export class DynamoDBAdapter implements StorageAdapter {
Keys: keys.map((k) => ({ pk: k })),
},
},
- })
+ }),
);
const items = result.Responses?.[this.tableName] ?? [];
@@ -114,7 +119,7 @@ export class DynamoDBAdapter implements StorageAdapter {
PutRequest: {
Item: this.serialize(key, entry),
},
- })
+ }),
);
// DynamoDB batch write supports max 25 items per request
@@ -125,7 +130,7 @@ export class DynamoDBAdapter implements StorageAdapter {
RequestItems: {
[this.tableName]: chunk,
},
- })
+ }),
);
}
}
@@ -138,7 +143,7 @@ export class DynamoDBAdapter implements StorageAdapter {
DeleteRequest: {
Key: { pk: k },
},
- })
+ }),
);
for (let i = 0; i < writeRequests.length; i += 25) {
@@ -148,7 +153,7 @@ export class DynamoDBAdapter implements StorageAdapter {
RequestItems: {
[this.tableName]: chunk,
},
- })
+ }),
);
}
@@ -165,7 +170,7 @@ export class DynamoDBAdapter implements StorageAdapter {
':pk': `USECASE#${useCase}`,
},
Limit: limit,
- })
+ }),
);
return (result.Items ?? [])
@@ -183,7 +188,7 @@ export class DynamoDBAdapter implements StorageAdapter {
':pk': `MODEL#${modelVersion}`,
},
Limit: limit,
- })
+ }),
);
return (result.Items ?? [])
@@ -204,7 +209,7 @@ export class DynamoDBAdapter implements StorageAdapter {
KeyConditionExpression: 'gsi1pk = :pk',
ExpressionAttributeValues: { ':pk': `USECASE#${criteria.useCase}` },
ExclusiveStartKey: lastKey,
- })
+ }),
);
for (const item of result.Items ?? []) {
const entry = this.deserialize(item);
@@ -228,7 +233,7 @@ export class DynamoDBAdapter implements StorageAdapter {
KeyConditionExpression: 'gsi2pk = :pk',
ExpressionAttributeValues: { ':pk': `MODEL#${criteria.modelVersion}` },
ExclusiveStartKey: lastKey,
- })
+ }),
);
for (const item of result.Items ?? []) {
const entry = this.deserialize(item);
@@ -246,7 +251,7 @@ export class DynamoDBAdapter implements StorageAdapter {
let lastKey: Record | undefined;
do {
const result = await this.client.send(
- new ScanCommand({ TableName: this.tableName, ExclusiveStartKey: lastKey })
+ new ScanCommand({ TableName: this.tableName, ExclusiveStartKey: lastKey }),
);
for (const item of result.Items ?? []) {
const entry = this.deserialize(item);
@@ -264,7 +269,7 @@ export class DynamoDBAdapter implements StorageAdapter {
async getStats(): Promise {
try {
const result = await this.rawClient.send(
- new DescribeTableCommand({ TableName: this.tableName })
+ new DescribeTableCommand({ TableName: this.tableName }),
);
const itemCount = result.Table?.ItemCount ?? 0;
const sizeBytes = result.Table?.TableSizeBytes ?? 0;
@@ -290,7 +295,7 @@ export class DynamoDBAdapter implements StorageAdapter {
new ScanCommand({
TableName: this.tableName,
Limit: 1,
- })
+ }),
);
return { healthy: true };
} catch (error) {
@@ -348,7 +353,7 @@ export class DynamoDBAdapter implements StorageAdapter {
try {
createdAt = new Date(String(metadata.createdAt));
expiresAt = new Date(String(metadata.expiresAt));
- if (isNaN(createdAt.getTime()) || isNaN(expiresAt.getTime())) {
+ if (Number.isNaN(createdAt.getTime()) || Number.isNaN(expiresAt.getTime())) {
createdAt = new Date();
expiresAt = new Date(Date.now() - 1);
}
@@ -370,8 +375,12 @@ export class DynamoDBAdapter implements StorageAdapter {
embeddingDimensions: Number(item.embeddingDimensions ?? 0),
useCase: String(item.useCase ?? ''),
sensitive: Boolean(item.sensitive),
- tokens: this.coerceTokenCost(item.tokens as Partial<{ prompt: number; completion: number; total: number }> | undefined),
- cost: this.coerceTokenCost(item.cost as Partial<{ prompt: number; completion: number; total: number }> | undefined),
+ tokens: this.coerceTokenCost(
+ item.tokens as Partial<{ prompt: number; completion: number; total: number }> | undefined,
+ ),
+ cost: this.coerceTokenCost(
+ item.cost as Partial<{ prompt: number; completion: number; total: number }> | undefined,
+ ),
metadata: {
createdAt,
ttl: Number(metadata.ttl) || 0,
@@ -382,7 +391,11 @@ export class DynamoDBAdapter implements StorageAdapter {
};
}
- private coerceTokenCost(obj?: Partial<{ prompt: number; completion: number; total: number }>): { prompt: number; completion: number; total: number } {
+ private coerceTokenCost(obj?: Partial<{ prompt: number; completion: number; total: number }>): {
+ prompt: number;
+ completion: number;
+ total: number;
+ } {
if (!obj || typeof obj !== 'object') return { prompt: 0, completion: 0, total: 0 };
const prompt = typeof obj.prompt === 'number' ? obj.prompt : 0;
const completion = typeof obj.completion === 'number' ? obj.completion : 0;
diff --git a/packages/adapters/qdrant/package.json b/packages/adapters/qdrant/package.json
index 5c111c8..f250a89 100644
--- a/packages/adapters/qdrant/package.json
+++ b/packages/adapters/qdrant/package.json
@@ -20,7 +20,13 @@
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
- "keywords": ["llm-cache", "qdrant", "vector", "semantic-search", "adapter"],
+ "keywords": [
+ "llm-cache",
+ "qdrant",
+ "vector",
+ "semantic-search",
+ "adapter"
+ ],
"files": [
"dist",
"README.md"
diff --git a/packages/adapters/qdrant/src/QdrantAdapter.test.ts b/packages/adapters/qdrant/src/QdrantAdapter.test.ts
index 1f30bd4..10e4744 100644
--- a/packages/adapters/qdrant/src/QdrantAdapter.test.ts
+++ b/packages/adapters/qdrant/src/QdrantAdapter.test.ts
@@ -1,7 +1,7 @@
-import { describe, it, expect, vi, beforeEach } from 'vitest';
+import type { CacheEntry } from '@reaatech/llm-cache';
import { v5 as uuidv5 } from 'uuid';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
import { QdrantAdapter } from './QdrantAdapter.js';
-import type { CacheEntry } from '@reaatech/llm-cache';
const KEY_NAMESPACE = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
const pointId = (key: string) => uuidv5(key, KEY_NAMESPACE);
@@ -92,7 +92,7 @@ describe('QdrantAdapter', () => {
collectionName: 'test-cache',
vectorSize: 3,
});
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ // biome-ignore lint/suspicious/noExplicitAny: test mock injection
(adapter as any).client = mockClient as any;
});
@@ -128,10 +128,10 @@ describe('QdrantAdapter', () => {
const result = await adapter.get('key');
expect(result).not.toBeNull();
- expect(result!.prompt).toBe('test');
+ expect(result?.prompt).toBe('test');
expect(mockClient.retrieve).toHaveBeenCalledWith(
'test-cache',
- expect.objectContaining({ ids: [pointId('key')] })
+ expect.objectContaining({ ids: [pointId('key')] }),
);
});
@@ -202,7 +202,9 @@ describe('QdrantAdapter', () => {
it('should pass olderThan as a numeric range filter', async () => {
mockClient.scroll.mockResolvedValueOnce({ points: [], next_page_offset: null });
await adapter.invalidateByCriteria({ olderThan: new Date(1700_000_000_000) });
- const filter = mockClient.scroll.mock.calls[0][1].filter as { must: Array<{ range?: unknown }> };
+ const filter = mockClient.scroll.mock.calls[0][1].filter as {
+ must: Array<{ range?: unknown }>;
+ };
expect(filter.must.some((c) => c.range)).toBe(true);
});
diff --git a/packages/adapters/qdrant/src/QdrantAdapter.ts b/packages/adapters/qdrant/src/QdrantAdapter.ts
index 852f999..bb2c3f5 100644
--- a/packages/adapters/qdrant/src/QdrantAdapter.ts
+++ b/packages/adapters/qdrant/src/QdrantAdapter.ts
@@ -1,14 +1,14 @@
import { QdrantClient } from '@qdrant/js-client-rest';
-import { v5 as uuidv5 } from 'uuid';
import type {
CacheEntry,
- InvalidationCriteria,
- StorageStats,
HealthStatus,
+ InvalidationCriteria,
SimilarityResult,
+ StorageStats,
VectorSearchFilters,
+ VectorStorageAdapter,
} from '@reaatech/llm-cache';
-import type { VectorStorageAdapter } from '@reaatech/llm-cache';
+import { v5 as uuidv5 } from 'uuid';
// Stable namespace UUID so the same key always maps to the same point ID across processes.
const KEY_NAMESPACE = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
@@ -174,18 +174,14 @@ export class QdrantAdapter implements VectorStorageAdapter {
}
async findByUseCase(useCase: string, limit = 100): Promise {
- return this.scrollAll(
- { must: [{ key: 'useCase', match: { value: useCase } }] },
- limit,
- true
- );
+ return this.scrollAll({ must: [{ key: 'useCase', match: { value: useCase } }] }, limit, true);
}
async findByModelVersion(modelVersion: string, limit = 100): Promise {
return this.scrollAll(
{ must: [{ key: 'modelVersion', match: { value: modelVersion } }] },
limit,
- true
+ true,
);
}
@@ -193,7 +189,7 @@ export class QdrantAdapter implements VectorStorageAdapter {
embedding: number[],
threshold: number,
filters: VectorSearchFilters,
- limit = 10
+ limit = 10,
): Promise {
const mustConditions: Array> = [];
@@ -227,7 +223,7 @@ export class QdrantAdapter implements VectorStorageAdapter {
return result
.map((hit) => ({
- entry: this.deserializeEntry(hit.payload!, (hit.vector as number[]) ?? []),
+ entry: this.deserializeEntry(hit.payload ?? {}, (hit.vector as number[]) ?? []),
similarity: hit.score,
}))
.filter((r) => !this.isExpired(r.entry));
@@ -319,7 +315,7 @@ export class QdrantAdapter implements VectorStorageAdapter {
private async scrollAll(
filter: Record,
limit: number,
- withVector: boolean
+ withVector: boolean,
): Promise {
const out: CacheEntry[] = [];
let offset: string | number | undefined;
@@ -391,7 +387,7 @@ export class QdrantAdapter implements VectorStorageAdapter {
try {
createdAt = new Date(String(metadata.createdAt));
expiresAt = new Date(String(metadata.expiresAt));
- if (isNaN(createdAt.getTime()) || isNaN(expiresAt.getTime())) {
+ if (Number.isNaN(createdAt.getTime()) || Number.isNaN(expiresAt.getTime())) {
createdAt = new Date();
expiresAt = new Date(Date.now() - 1);
}
@@ -413,8 +409,14 @@ export class QdrantAdapter implements VectorStorageAdapter {
embeddingDimensions: Number(payload.embeddingDimensions),
useCase: String(payload.useCase),
sensitive: Boolean(payload.sensitive),
- tokens: this.coerceTokenCost(payload.tokens as Partial<{ prompt: number; completion: number; total: number }> | undefined),
- cost: this.coerceTokenCost(payload.cost as Partial<{ prompt: number; completion: number; total: number }> | undefined),
+ tokens: this.coerceTokenCost(
+ payload.tokens as
+ | Partial<{ prompt: number; completion: number; total: number }>
+ | undefined,
+ ),
+ cost: this.coerceTokenCost(
+ payload.cost as Partial<{ prompt: number; completion: number; total: number }> | undefined,
+ ),
metadata: {
createdAt,
ttl: Number(metadata.ttl) || 0,
@@ -425,7 +427,11 @@ export class QdrantAdapter implements VectorStorageAdapter {
};
}
- private coerceTokenCost(obj?: Partial<{ prompt: number; completion: number; total: number }>): { prompt: number; completion: number; total: number } {
+ private coerceTokenCost(obj?: Partial<{ prompt: number; completion: number; total: number }>): {
+ prompt: number;
+ completion: number;
+ total: number;
+ } {
if (!obj || typeof obj !== 'object') return { prompt: 0, completion: 0, total: 0 };
const prompt = typeof obj.prompt === 'number' ? obj.prompt : 0;
const completion = typeof obj.completion === 'number' ? obj.completion : 0;
diff --git a/packages/adapters/redis/package.json b/packages/adapters/redis/package.json
index f583261..a040905 100644
--- a/packages/adapters/redis/package.json
+++ b/packages/adapters/redis/package.json
@@ -20,7 +20,12 @@
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
- "keywords": ["llm-cache", "redis", "cache", "adapter"],
+ "keywords": [
+ "llm-cache",
+ "redis",
+ "cache",
+ "adapter"
+ ],
"files": [
"dist",
"README.md"
diff --git a/packages/adapters/redis/src/RedisAdapter.test.ts b/packages/adapters/redis/src/RedisAdapter.test.ts
index e8e59bc..0411520 100644
--- a/packages/adapters/redis/src/RedisAdapter.test.ts
+++ b/packages/adapters/redis/src/RedisAdapter.test.ts
@@ -1,6 +1,6 @@
-import { describe, it, expect, vi, beforeEach } from 'vitest';
-import { RedisAdapter } from './RedisAdapter.js';
import type { CacheEntry } from '@reaatech/llm-cache';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+import { RedisAdapter } from './RedisAdapter.js';
function makeEntry(overrides?: Partial): CacheEntry {
const now = new Date();
@@ -68,7 +68,7 @@ describe('RedisAdapter', () => {
};
adapter = new RedisAdapter({ url: 'redis://localhost:6379' });
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ // biome-ignore lint/suspicious/noExplicitAny: test mock injection
(adapter as any).client = mockClient as any;
});
@@ -94,8 +94,8 @@ describe('RedisAdapter', () => {
mockClient.get.mockResolvedValueOnce(JSON.stringify(entry));
const result = await adapter.get('key');
expect(result).not.toBeNull();
- expect(result!.prompt).toBe('test');
- expect(result!.metadata.createdAt instanceof Date).toBe(true);
+ expect(result?.prompt).toBe('test');
+ expect(result?.metadata.createdAt instanceof Date).toBe(true);
});
it('should return null for missing key', async () => {
diff --git a/packages/adapters/redis/src/RedisAdapter.ts b/packages/adapters/redis/src/RedisAdapter.ts
index 03ac26e..0472913 100644
--- a/packages/adapters/redis/src/RedisAdapter.ts
+++ b/packages/adapters/redis/src/RedisAdapter.ts
@@ -1,6 +1,11 @@
+import type {
+ CacheEntry,
+ HealthStatus,
+ InvalidationCriteria,
+ StorageAdapter,
+ StorageStats,
+} from '@reaatech/llm-cache';
import { createClient, type RedisClientType } from 'redis';
-import type { CacheEntry, InvalidationCriteria, StorageStats, HealthStatus } from '@reaatech/llm-cache';
-import type { StorageAdapter } from '@reaatech/llm-cache';
export interface RedisAdapterConfig {
url: string;
diff --git a/packages/core/src/cache/CacheEngine.integration.test.ts b/packages/core/src/cache/CacheEngine.integration.test.ts
index f583a62..7508567 100644
--- a/packages/core/src/cache/CacheEngine.integration.test.ts
+++ b/packages/core/src/cache/CacheEngine.integration.test.ts
@@ -1,6 +1,6 @@
-import { describe, it, expect, beforeEach } from 'vitest';
-import { CacheEngine, InMemoryAdapter } from '@reaatech/llm-cache';
import type { CacheConfig, EmbeddingProvider } from '@reaatech/llm-cache';
+import { CacheEngine, InMemoryAdapter } from '@reaatech/llm-cache';
+import { beforeEach, describe, expect, it } from 'vitest';
class DeterministicEmbedder implements EmbeddingProvider {
embed(text: string): Promise {
@@ -79,7 +79,7 @@ describe('CacheEngine Integration', () => {
await engine.set(
'What is the capital of France?',
{ answer: 'Paris' },
- { model: 'gpt-4', modelVersion: 'gpt-4-0613' }
+ { model: 'gpt-4', modelVersion: 'gpt-4-0613' },
);
// Query with a semantically similar but different prompt
@@ -100,7 +100,7 @@ describe('CacheEngine Integration', () => {
await engine.set(
'What is TypeScript?',
{ answer: 'A typed superset of JavaScript' },
- { model: 'gpt-4', modelVersion: 'gpt-4-0613' }
+ { model: 'gpt-4', modelVersion: 'gpt-4-0613' },
);
const result = await engine.get('What is TypeScript?', {
@@ -118,7 +118,7 @@ describe('CacheEngine Integration', () => {
await engine.set(
'classify: spam',
{ label: 'spam' },
- { model: 'gpt-4', modelVersion: 'gpt-4-0613', useCase: 'classification' }
+ { model: 'gpt-4', modelVersion: 'gpt-4-0613', useCase: 'classification' },
);
// Same prompt in a different use case should miss
@@ -136,7 +136,7 @@ describe('CacheEngine Integration', () => {
'expires soon',
'value',
{ model: 'gpt-4', modelVersion: 'gpt-4-0613' },
- { ttl: 0 } // expires immediately
+ { ttl: 0 }, // expires immediately
);
// Manually expire the entry by setting expiresAt in the past
@@ -169,17 +169,17 @@ describe('CacheEngine Integration', () => {
await engine.set(
'What is JavaScript?',
{ answer: 'JS' },
- { model: 'gpt-4', modelVersion: 'gpt-4-0613' }
+ { model: 'gpt-4', modelVersion: 'gpt-4-0613' },
);
await engine.set(
'What is Python?',
{ answer: 'PY' },
- { model: 'gpt-4', modelVersion: 'gpt-4-0613' }
+ { model: 'gpt-4', modelVersion: 'gpt-4-0613' },
);
await engine.set(
'What is Rust?',
{ answer: 'RS' },
- { model: 'gpt-4', modelVersion: 'gpt-4-0613' }
+ { model: 'gpt-4', modelVersion: 'gpt-4-0613' },
);
const result = await engine.get('Tell me about JavaScript', {
diff --git a/packages/core/src/cache/CacheEngine.test.ts b/packages/core/src/cache/CacheEngine.test.ts
index fafaa1b..5d356b9 100644
--- a/packages/core/src/cache/CacheEngine.test.ts
+++ b/packages/core/src/cache/CacheEngine.test.ts
@@ -1,8 +1,8 @@
-import { describe, it, expect, beforeEach } from 'vitest';
-import { CacheEngine } from './CacheEngine.js';
-import { InMemoryAdapter } from '../storage/InMemoryAdapter.js';
-import type { EmbeddingProvider } from '../embedding/EmbeddingProvider.js';
+import { beforeEach, describe, expect, it } from 'vitest';
import type { CacheConfig } from '../config/CacheConfig.js';
+import type { EmbeddingProvider } from '../embedding/EmbeddingProvider.js';
+import { InMemoryAdapter } from '../storage/InMemoryAdapter.js';
+import { CacheEngine } from './CacheEngine.js';
class FakeEmbedder implements EmbeddingProvider {
private dimension: number;
@@ -15,7 +15,7 @@ class FakeEmbedder implements EmbeddingProvider {
return Promise.resolve(
Array(this.dimension)
.fill(0)
- .map(() => Math.random())
+ .map(() => Math.random()),
);
}
@@ -94,7 +94,7 @@ describe('CacheEngine', () => {
await engine.set(
'hello world',
{ content: 'hi' },
- { model: 'gpt-4', modelVersion: 'gpt-4-0613' }
+ { model: 'gpt-4', modelVersion: 'gpt-4-0613' },
);
const result = await engine.get('hello world', { model: 'gpt-4', modelVersion: 'gpt-4-0613' });
@@ -112,7 +112,7 @@ describe('CacheEngine', () => {
model: 'gpt-4',
modelVersion: 'gpt-4-0613',
temperature: 0.5,
- }
+ },
);
// Same prompt, different temperature = different fingerprint = miss
@@ -128,7 +128,7 @@ describe('CacheEngine', () => {
await engine.set(
'hello world',
{ content: 'hi' },
- { model: 'gpt-4', modelVersion: 'gpt-4-0613' }
+ { model: 'gpt-4', modelVersion: 'gpt-4-0613' },
);
const result = await engine.invalidate({ useCase: 'general' });
@@ -162,7 +162,7 @@ describe('CacheEngine', () => {
if (result.hit) {
expect(typeof result.entry.response).toBe('object');
expect((result.entry.response as typeof complexResponse).choices[0].message.content).toBe(
- 'hi'
+ 'hi',
);
}
});
@@ -175,7 +175,7 @@ describe('CacheEngine', () => {
model: 'gpt-4',
modelVersion: 'gpt-4-0613',
},
- { queryType: 'factual' }
+ { queryType: 'factual' },
);
expect(entry.metadata.ttl).toBe(1800);
@@ -189,7 +189,7 @@ describe('CacheEngine', () => {
model: 'gpt-4',
modelVersion: 'gpt-4-0613',
},
- { sensitive: true }
+ { sensitive: true },
);
expect(entry.metadata.ttl).toBe(600);
@@ -247,7 +247,7 @@ describe('CacheEngine', () => {
'prompt',
'response',
{ model: 'gpt-4', modelVersion: 'gpt-4-0613' },
- { tokens: { prompt: 100, completion: 200 } }
+ { tokens: { prompt: 100, completion: 200 } },
);
expect(entry.tokens.total).toBe(300);
expect(entry.cost.total).toBeCloseTo(0.003);
diff --git a/packages/core/src/cache/CacheEngine.ts b/packages/core/src/cache/CacheEngine.ts
index 0ac9a56..8df9fcf 100644
--- a/packages/core/src/cache/CacheEngine.ts
+++ b/packages/core/src/cache/CacheEngine.ts
@@ -1,18 +1,18 @@
+import { v4 as uuidv4 } from 'uuid';
+import type { CacheConfig } from '../config/CacheConfig.js';
+import type { EmbeddingProvider } from '../embedding/EmbeddingProvider.js';
+import type { StorageAdapter, VectorStorageAdapter } from '../storage/StorageAdapter.js';
import type {
CacheEntry,
- CacheResult,
- CacheOptions,
CacheMetadata,
- VectorSearchFilters,
+ CacheOptions,
+ CacheResult,
CostCalculatorLike,
InvalidateResult,
+ VectorSearchFilters,
} from '../types/index.js';
-import type { StorageAdapter, VectorStorageAdapter } from '../storage/StorageAdapter.js';
-import type { EmbeddingProvider } from '../embedding/EmbeddingProvider.js';
-import type { CacheConfig } from '../config/CacheConfig.js';
-import type { EncryptionService, EncryptedPayload } from '../utils/encryption.js';
-import { buildPromptHash, buildCacheFingerprint, buildExactMatchKey } from '../utils/hash.js';
-import { v4 as uuidv4 } from 'uuid';
+import type { EncryptedPayload, EncryptionService } from '../utils/encryption.js';
+import { buildCacheFingerprint, buildExactMatchKey, buildPromptHash } from '../utils/hash.js';
export interface CacheEngineDependencies {
storage: StorageAdapter;
@@ -98,7 +98,7 @@ export class CacheEngine {
embedding,
this.config.similarity.threshold,
filters,
- this.config.similarity.maxResults
+ this.config.similarity.maxResults,
);
const fresh = similarEntries.filter((r) => !this.isExpired(r.entry));
@@ -128,15 +128,15 @@ export class CacheEngine {
}
async getBatch(
- prompts: Array<{ prompt: string; options?: CacheOptions }>
+ prompts: Array<{ prompt: string; options?: CacheOptions }>,
): Promise> {
const settled = await Promise.allSettled(
- prompts.map(({ prompt, options }) => this.get(prompt, options))
+ prompts.map(({ prompt, options }) => this.get(prompt, options)),
);
return settled.map((s) =>
s.status === 'fulfilled'
? s.value
- : ({ hit: false, reason: 'error', error: errorMessage(s.reason) } as const)
+ : ({ hit: false, reason: 'error', error: errorMessage(s.reason) } as const),
);
}
@@ -144,7 +144,7 @@ export class CacheEngine {
prompt: string,
response: unknown,
options?: CacheOptions,
- metadata?: CacheMetadata
+ metadata?: CacheMetadata,
): Promise {
if (!prompt) {
throw new Error('Prompt must be a non-empty string');
@@ -181,11 +181,7 @@ export class CacheEngine {
total: (metadata?.tokens?.prompt ?? 0) + (metadata?.tokens?.completion ?? 0),
};
- const cost = this.calculateCost(
- options?.model ?? 'unknown',
- tokens.prompt,
- tokens.completion
- );
+ const cost = this.calculateCost(options?.model ?? 'unknown', tokens.prompt, tokens.completion);
const entry: CacheEntry = {
id: uuidv4(),
@@ -225,17 +221,17 @@ export class CacheEngine {
response: unknown;
options?: CacheOptions;
metadata?: CacheMetadata;
- }>
+ }>,
): Promise> {
const settled = await Promise.allSettled(
items.map(({ prompt, response, options, metadata }) =>
- this.set(prompt, response, options, metadata)
- )
+ this.set(prompt, response, options, metadata),
+ ),
);
return settled.map((s) =>
s.status === 'fulfilled'
? ({ ok: true, entry: s.value } as const)
- : ({ ok: false, error: errorMessage(s.reason) } as const)
+ : ({ ok: false, error: errorMessage(s.reason) } as const),
);
}
@@ -297,7 +293,7 @@ export class CacheEngine {
private calculateCost(
model: string,
promptTokens: number,
- completionTokens: number
+ completionTokens: number,
): { prompt: number; completion: number; total: number } {
if (!this.config.cost.enabled || !this.costCalculator) {
return { prompt: 0, completion: 0, total: 0 };
@@ -306,7 +302,7 @@ export class CacheEngine {
model,
promptTokens,
completionTokens,
- this.config.cost.currency
+ this.config.cost.currency,
);
return {
prompt: breakdown.inputCost,
diff --git a/packages/core/src/cache/SimilarityMatcher.test.ts b/packages/core/src/cache/SimilarityMatcher.test.ts
index 1e7a62b..d3383bd 100644
--- a/packages/core/src/cache/SimilarityMatcher.test.ts
+++ b/packages/core/src/cache/SimilarityMatcher.test.ts
@@ -1,7 +1,7 @@
-import { describe, it, expect, vi } from 'vitest';
-import { SimilarityMatcher } from './SimilarityMatcher.js';
+import { describe, expect, it, vi } from 'vitest';
import type { VectorStorageAdapter } from '../storage/StorageAdapter.js';
import type { CacheEntry } from '../types/index.js';
+import { SimilarityMatcher } from './SimilarityMatcher.js';
function makeEntry(embedding: number[], overrides?: Partial): CacheEntry {
const now = new Date();
@@ -107,7 +107,7 @@ describe('SimilarityMatcher', () => {
it('should throw on dimension mismatch', () => {
const matcher = new SimilarityMatcher({} as VectorStorageAdapter);
expect(() => matcher.calculateCosineSimilarity([1, 0], [1, 0, 0])).toThrow(
- 'dimension mismatch'
+ 'dimension mismatch',
);
});
});
diff --git a/packages/core/src/cache/SimilarityMatcher.ts b/packages/core/src/cache/SimilarityMatcher.ts
index e86f65a..6ccbbfa 100644
--- a/packages/core/src/cache/SimilarityMatcher.ts
+++ b/packages/core/src/cache/SimilarityMatcher.ts
@@ -1,5 +1,5 @@
-import type { SimilarityResult, VectorSearchFilters, CacheEntry } from '../types/index.js';
import type { VectorStorageAdapter } from '../storage/StorageAdapter.js';
+import type { CacheEntry, SimilarityResult, VectorSearchFilters } from '../types/index.js';
export class SimilarityMatcher {
constructor(private vectorStorage: VectorStorageAdapter) {}
@@ -8,7 +8,7 @@ export class SimilarityMatcher {
embedding: number[],
filters: VectorSearchFilters,
threshold: number,
- limit = 10
+ limit = 10,
): Promise {
const results = await this.vectorStorage.findSimilar(embedding, threshold, filters, limit);
diff --git a/packages/core/src/config/CacheConfig.test.ts b/packages/core/src/config/CacheConfig.test.ts
index 54b8271..d8d2ff7 100644
--- a/packages/core/src/config/CacheConfig.test.ts
+++ b/packages/core/src/config/CacheConfig.test.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect } from 'vitest';
+import { describe, expect, it } from 'vitest';
import { CacheConfigSchema } from './CacheConfig.js';
describe('CacheConfigSchema', () => {
@@ -108,7 +108,7 @@ describe('CacheConfigSchema', () => {
CacheConfigSchema.parse({
storage: { adapter: 'postgres' },
vectorStorage: { adapter: 'memory' },
- })
+ }),
).toThrow();
});
@@ -118,7 +118,7 @@ describe('CacheConfigSchema', () => {
storage: { adapter: 'memory' },
vectorStorage: { adapter: 'memory' },
similarity: { threshold: 1.5 },
- })
+ }),
).toThrow();
});
@@ -128,7 +128,7 @@ describe('CacheConfigSchema', () => {
storage: { adapter: 'memory' },
vectorStorage: { adapter: 'memory' },
observability: { logging: 'verbose' },
- })
+ }),
).toThrow();
});
});
diff --git a/packages/core/src/config/index.ts b/packages/core/src/config/index.ts
index 44264a0..169c13b 100644
--- a/packages/core/src/config/index.ts
+++ b/packages/core/src/config/index.ts
@@ -1 +1 @@
-export { CacheConfigSchema, type CacheConfig } from './CacheConfig.js';
+export { type CacheConfig, CacheConfigSchema } from './CacheConfig.js';
diff --git a/packages/core/src/embedding/OpenAIEmbedder.test.ts b/packages/core/src/embedding/OpenAIEmbedder.test.ts
index 0244e7a..503ea58 100644
--- a/packages/core/src/embedding/OpenAIEmbedder.test.ts
+++ b/packages/core/src/embedding/OpenAIEmbedder.test.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { OpenAIEmbedder } from './OpenAIEmbedder.js';
describe('OpenAIEmbedder', () => {
@@ -18,7 +18,7 @@ describe('OpenAIEmbedder', () => {
vi.stubGlobal('fetch', fetchMock);
vi.spyOn(
embedder as unknown as { sleep: (ms: number) => Promise },
- 'sleep'
+ 'sleep',
).mockResolvedValue();
});
@@ -141,7 +141,7 @@ describe('OpenAIEmbedder', () => {
mockResponse([
[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6],
- ])
+ ]),
);
const results = await embedder.embedBatch(['hello', 'world']);
diff --git a/packages/core/src/embedding/OpenAIEmbedder.ts b/packages/core/src/embedding/OpenAIEmbedder.ts
index f234db9..e633ac6 100644
--- a/packages/core/src/embedding/OpenAIEmbedder.ts
+++ b/packages/core/src/embedding/OpenAIEmbedder.ts
@@ -37,7 +37,7 @@ export class OpenAIEmbedder implements EmbeddingProvider {
if (cached) {
if (expectedDimensions && cached.length !== expectedDimensions) {
throw new Error(
- `Embedding dimension mismatch: cached=${cached.length}, expected=${expectedDimensions}`
+ `Embedding dimension mismatch: cached=${cached.length}, expected=${expectedDimensions}`,
);
}
// Refresh LRU position
@@ -64,7 +64,7 @@ export class OpenAIEmbedder implements EmbeddingProvider {
if (cached) {
if (expectedDimensions && cached.length !== expectedDimensions) {
throw new Error(
- `Embedding dimension mismatch: cached=${cached.length}, expected=${expectedDimensions}`
+ `Embedding dimension mismatch: cached=${cached.length}, expected=${expectedDimensions}`,
);
}
this.cache.delete(cacheKey);
@@ -91,10 +91,7 @@ export class OpenAIEmbedder implements EmbeddingProvider {
return out;
}
- private async fetchEmbeddings(
- texts: string[],
- expectedDimensions?: number
- ): Promise {
+ private async fetchEmbeddings(texts: string[], expectedDimensions?: number): Promise {
let lastError: Error | undefined;
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
@@ -147,7 +144,7 @@ export class OpenAIEmbedder implements EmbeddingProvider {
for (const e of embeddings) {
if (e.length !== expectedDimensions) {
throw new Error(
- `Embedding dimension mismatch: received=${e.length}, expected=${expectedDimensions}`
+ `Embedding dimension mismatch: received=${e.length}, expected=${expectedDimensions}`,
);
}
}
@@ -184,7 +181,7 @@ export class OpenAIEmbedder implements EmbeddingProvider {
}
private backoffMs(attempt: number): number {
- return Math.pow(2, attempt) * 1000;
+ return 2 ** attempt * 1000;
}
private sleep(ms: number): Promise {
diff --git a/packages/core/src/embedding/index.ts b/packages/core/src/embedding/index.ts
index a68dd13..e6ecc6e 100644
--- a/packages/core/src/embedding/index.ts
+++ b/packages/core/src/embedding/index.ts
@@ -1,2 +1,2 @@
-export { type EmbeddingProvider, type EmbeddingProviderConfig } from './EmbeddingProvider.js';
+export type { EmbeddingProvider, EmbeddingProviderConfig } from './EmbeddingProvider.js';
export { OpenAIEmbedder } from './OpenAIEmbedder.js';
diff --git a/packages/core/src/index.test.ts b/packages/core/src/index.test.ts
index 26e5fa5..edbbf90 100644
--- a/packages/core/src/index.test.ts
+++ b/packages/core/src/index.test.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect } from 'vitest';
+import { describe, expect, it } from 'vitest';
import * as core from './index.js';
describe('core exports', () => {
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 2636056..9ee1fa1 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -1,10 +1,10 @@
-export * from './types/index.js';
-export * from './storage/StorageAdapter.js';
-export * from './storage/InMemoryAdapter.js';
+export * from './cache/CacheEngine.js';
+export * from './cache/SimilarityMatcher.js';
+export * from './config/CacheConfig.js';
export * from './embedding/EmbeddingProvider.js';
export * from './embedding/OpenAIEmbedder.js';
-export * from './config/CacheConfig.js';
-export * from './utils/hash.js';
+export * from './storage/InMemoryAdapter.js';
+export * from './storage/StorageAdapter.js';
+export * from './types/index.js';
export * from './utils/encryption.js';
-export * from './cache/CacheEngine.js';
-export * from './cache/SimilarityMatcher.js';
+export * from './utils/hash.js';
diff --git a/packages/core/src/storage/InMemoryAdapter.test.ts b/packages/core/src/storage/InMemoryAdapter.test.ts
index 4fe2d28..36619c7 100644
--- a/packages/core/src/storage/InMemoryAdapter.test.ts
+++ b/packages/core/src/storage/InMemoryAdapter.test.ts
@@ -1,6 +1,6 @@
-import { describe, it, expect, beforeEach } from 'vitest';
-import { InMemoryAdapter } from './InMemoryAdapter.js';
+import { beforeEach, describe, expect, it } from 'vitest';
import type { CacheEntry } from '../types/index.js';
+import { InMemoryAdapter } from './InMemoryAdapter.js';
function makeEntry(embedding: number[], overrides?: Partial): CacheEntry {
const now = new Date();
@@ -42,7 +42,7 @@ describe('InMemoryAdapter', () => {
const retrieved = await adapter.get('key1');
expect(retrieved).not.toBeNull();
- expect(retrieved!.prompt).toBe('test prompt');
+ expect(retrieved?.prompt).toBe('test prompt');
});
it('should return null for missing keys', async () => {
@@ -107,5 +107,4 @@ describe('InMemoryAdapter', () => {
const stats = await smallAdapter.getStats();
expect(stats.totalEntries).toBe(2);
});
-
});
diff --git a/packages/core/src/storage/InMemoryAdapter.ts b/packages/core/src/storage/InMemoryAdapter.ts
index 8b83f5e..2640523 100644
--- a/packages/core/src/storage/InMemoryAdapter.ts
+++ b/packages/core/src/storage/InMemoryAdapter.ts
@@ -1,9 +1,9 @@
import type {
CacheEntry,
- InvalidationCriteria,
- StorageStats,
HealthStatus,
+ InvalidationCriteria,
SimilarityResult,
+ StorageStats,
VectorSearchFilters,
} from '../types/index.js';
import type { VectorStorageAdapter } from './StorageAdapter.js';
@@ -52,7 +52,7 @@ export class InMemoryAdapter implements VectorStorageAdapter {
if (deleted && entry) {
this.stats.totalSizeBytes = Math.max(
0,
- this.stats.totalSizeBytes - JSON.stringify(entry).length
+ this.stats.totalSizeBytes - JSON.stringify(entry).length,
);
}
return Promise.resolve(deleted);
@@ -112,7 +112,7 @@ export class InMemoryAdapter implements VectorStorageAdapter {
embedding: number[],
threshold: number,
filters: VectorSearchFilters,
- limit = 10
+ limit = 10,
): Promise {
const results: SimilarityResult[] = [];
@@ -157,7 +157,7 @@ export class InMemoryAdapter implements VectorStorageAdapter {
if (match) {
this.stats.totalSizeBytes = Math.max(
0,
- this.stats.totalSizeBytes - JSON.stringify(entry).length
+ this.stats.totalSizeBytes - JSON.stringify(entry).length,
);
this.cache.delete(key);
count++;
diff --git a/packages/core/src/storage/StorageAdapter.ts b/packages/core/src/storage/StorageAdapter.ts
index 8f5e32f..4e601c0 100644
--- a/packages/core/src/storage/StorageAdapter.ts
+++ b/packages/core/src/storage/StorageAdapter.ts
@@ -1,9 +1,9 @@
import type {
CacheEntry,
- InvalidationCriteria,
- StorageStats,
HealthStatus,
+ InvalidationCriteria,
SimilarityResult,
+ StorageStats,
VectorSearchFilters,
} from '../types/index.js';
@@ -39,6 +39,6 @@ export interface VectorStorageAdapter extends StorageAdapter {
embedding: number[],
threshold: number,
filters: VectorSearchFilters,
- limit?: number
+ limit?: number,
): Promise;
}
diff --git a/packages/core/src/storage/index.ts b/packages/core/src/storage/index.ts
index a9209ce..50d5cb5 100644
--- a/packages/core/src/storage/index.ts
+++ b/packages/core/src/storage/index.ts
@@ -1,2 +1,2 @@
-export { type StorageAdapter, type VectorStorageAdapter } from './StorageAdapter.js';
export { InMemoryAdapter, type InMemoryAdapterOptions } from './InMemoryAdapter.js';
+export type { StorageAdapter, VectorStorageAdapter } from './StorageAdapter.js';
diff --git a/packages/core/src/types/index.ts b/packages/core/src/types/index.ts
index b8f2bda..20de575 100644
--- a/packages/core/src/types/index.ts
+++ b/packages/core/src/types/index.ts
@@ -75,7 +75,7 @@ export interface CostCalculatorLike {
model: string,
promptTokens: number,
completionTokens: number,
- currency?: string
+ currency?: string,
): {
inputCost: number;
outputCost: number;
diff --git a/packages/core/src/utils/encryption.test.ts b/packages/core/src/utils/encryption.test.ts
index 05c4e7d..4d9c833 100644
--- a/packages/core/src/utils/encryption.test.ts
+++ b/packages/core/src/utils/encryption.test.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect } from 'vitest';
+import { describe, expect, it } from 'vitest';
import { EncryptionService } from './encryption.js';
describe('EncryptionService', () => {
@@ -38,7 +38,7 @@ describe('EncryptionService', () => {
it('should throw on tampered ciphertext', () => {
const encrypted = service.encrypt('secret');
- encrypted.ciphertext = encrypted.ciphertext.slice(0, -4) + 'dead';
+ encrypted.ciphertext = `${encrypted.ciphertext.slice(0, -4)}dead`;
expect(() => service.decrypt(encrypted)).toThrow();
});
});
diff --git a/packages/core/src/utils/encryption.ts b/packages/core/src/utils/encryption.ts
index b1a8588..92f81ae 100644
--- a/packages/core/src/utils/encryption.ts
+++ b/packages/core/src/utils/encryption.ts
@@ -24,9 +24,7 @@ export class EncryptionService {
this._key = keyOrPassphrase;
this._salt = Buffer.from(options.salt ?? randomBytes(SALT_LENGTH));
} else {
- this._salt = options.salt
- ? Buffer.from(options.salt)
- : randomBytes(SALT_LENGTH);
+ this._salt = options.salt ? Buffer.from(options.salt) : randomBytes(SALT_LENGTH);
this._key = scryptSync(String(keyOrPassphrase), this._salt, KEY_LENGTH);
}
}
diff --git a/packages/core/src/utils/hash.test.ts b/packages/core/src/utils/hash.test.ts
index 4aaedfd..321b39b 100644
--- a/packages/core/src/utils/hash.test.ts
+++ b/packages/core/src/utils/hash.test.ts
@@ -1,5 +1,5 @@
-import { describe, it, expect } from 'vitest';
-import { sha256, buildPromptHash, buildCacheFingerprint, buildExactMatchKey } from './hash.js';
+import { describe, expect, it } from 'vitest';
+import { buildCacheFingerprint, buildExactMatchKey, buildPromptHash, sha256 } from './hash.js';
describe('hash utilities', () => {
it('sha256 should produce a 64-character hex string', () => {
diff --git a/packages/core/src/utils/hash.ts b/packages/core/src/utils/hash.ts
index 8256ecd..5733dac 100644
--- a/packages/core/src/utils/hash.ts
+++ b/packages/core/src/utils/hash.ts
@@ -50,7 +50,7 @@ export function buildCacheFingerprint(options: {
export function buildExactMatchKey(
promptHash: string,
useCase: string,
- generationConfigHash: string
+ generationConfigHash: string,
): string {
// Hash the useCase to neutralize delimiter collisions and any user-supplied special chars.
const safeUseCase = sha256(useCase).slice(0, 16);
diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts
index 08f5216..b4275d9 100644
--- a/packages/core/src/utils/index.ts
+++ b/packages/core/src/utils/index.ts
@@ -1,2 +1,2 @@
-export { sha256, buildPromptHash, buildCacheFingerprint, buildExactMatchKey } from './hash.js';
export { EncryptionService } from './encryption.js';
+export { buildCacheFingerprint, buildExactMatchKey, buildPromptHash, sha256 } from './hash.js';
diff --git a/packages/cost-tracker/package.json b/packages/cost-tracker/package.json
index 9e20b65..cfb5bf6 100644
--- a/packages/cost-tracker/package.json
+++ b/packages/cost-tracker/package.json
@@ -20,7 +20,13 @@
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
- "keywords": ["llm-cache", "cost", "pricing", "openai", "anthropic"],
+ "keywords": [
+ "llm-cache",
+ "cost",
+ "pricing",
+ "openai",
+ "anthropic"
+ ],
"files": [
"dist",
"README.md"
diff --git a/packages/cost-tracker/src/CostCalculator.test.ts b/packages/cost-tracker/src/CostCalculator.test.ts
index 0c2fc24..445bb15 100644
--- a/packages/cost-tracker/src/CostCalculator.test.ts
+++ b/packages/cost-tracker/src/CostCalculator.test.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect, beforeEach } from 'vitest';
+import { beforeEach, describe, expect, it } from 'vitest';
import { CostCalculator } from './CostCalculator.js';
describe('CostCalculator', () => {
diff --git a/packages/cost-tracker/src/CostCalculator.ts b/packages/cost-tracker/src/CostCalculator.ts
index 95c9b54..220c6e6 100644
--- a/packages/cost-tracker/src/CostCalculator.ts
+++ b/packages/cost-tracker/src/CostCalculator.ts
@@ -40,7 +40,7 @@ export class CostCalculator {
model: string,
promptTokens: number,
completionTokens: number,
- currency = 'USD'
+ currency = 'USD',
): CostBreakdown {
const safePromptTokens = Math.max(0, promptTokens);
const safeCompletionTokens = Math.max(0, completionTokens);
@@ -76,7 +76,7 @@ export class CostCalculator {
calculateSavings(
originalCost: number,
- embeddingCost: number
+ embeddingCost: number,
): {
originalCost: number;
embeddingCost: number;
diff --git a/packages/cost-tracker/src/index.ts b/packages/cost-tracker/src/index.ts
index 766654b..f25251b 100644
--- a/packages/cost-tracker/src/index.ts
+++ b/packages/cost-tracker/src/index.ts
@@ -1,2 +1,2 @@
-export { CostCalculator, type CostBreakdown, type ModelPricing } from './CostCalculator.js';
+export { type CostBreakdown, CostCalculator, type ModelPricing } from './CostCalculator.js';
export { defaultPricingDatabase } from './pricing-data.js';
diff --git a/packages/observability/package.json b/packages/observability/package.json
index 0344433..8994052 100644
--- a/packages/observability/package.json
+++ b/packages/observability/package.json
@@ -20,7 +20,13 @@
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
- "keywords": ["llm-cache", "metrics", "logging", "prometheus", "observability"],
+ "keywords": [
+ "llm-cache",
+ "metrics",
+ "logging",
+ "prometheus",
+ "observability"
+ ],
"files": [
"dist",
"README.md"
diff --git a/packages/observability/src/index.ts b/packages/observability/src/index.ts
index e574b58..021a31f 100644
--- a/packages/observability/src/index.ts
+++ b/packages/observability/src/index.ts
@@ -1,2 +1,2 @@
-export { MetricsCollector, type MetricsCollectorConfig } from './metrics/MetricsCollector.js';
export { Logger, type LoggerConfig } from './logging/Logger.js';
+export { MetricsCollector, type MetricsCollectorConfig } from './metrics/MetricsCollector.js';
diff --git a/packages/observability/src/metrics/MetricsCollector.test.ts b/packages/observability/src/metrics/MetricsCollector.test.ts
index b2482c8..c4d9ebe 100644
--- a/packages/observability/src/metrics/MetricsCollector.test.ts
+++ b/packages/observability/src/metrics/MetricsCollector.test.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect, beforeEach } from 'vitest';
+import { beforeEach, describe, expect, it } from 'vitest';
import { MetricsCollector } from './MetricsCollector.js';
describe('MetricsCollector', () => {
diff --git a/packages/observability/src/metrics/MetricsCollector.ts b/packages/observability/src/metrics/MetricsCollector.ts
index d5dfe12..b66de9e 100644
--- a/packages/observability/src/metrics/MetricsCollector.ts
+++ b/packages/observability/src/metrics/MetricsCollector.ts
@@ -99,7 +99,7 @@ export class MetricsCollector {
lines.push(`${name}{quantile="0.95"} ${p95}`);
lines.push(`${name}{quantile="0.99"} ${p99}`);
}
- return lines.join('\n') + '\n';
+ return `${lines.join('\n')}\n`;
}
reset(): void {
diff --git a/packages/server/package.json b/packages/server/package.json
index 6b54694..d4b81c5 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -20,7 +20,12 @@
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
- "keywords": ["llm-cache", "server", "http", "cache"],
+ "keywords": [
+ "llm-cache",
+ "server",
+ "http",
+ "cache"
+ ],
"bin": {
"llm-cache-server": "./dist/cli.js"
},
diff --git a/packages/server/src/app.test.ts b/packages/server/src/app.test.ts
index 0615302..ac6dbc2 100644
--- a/packages/server/src/app.test.ts
+++ b/packages/server/src/app.test.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
+import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
import type { createApp } from './app.js';
const originalFetch = globalThis.fetch;
@@ -16,11 +16,11 @@ describe('Server App', () => {
if (url.includes('api.openai.com')) {
return new Response(
JSON.stringify({ data: [{ embedding: new Array(1536).fill(0.1) }] }),
- { status: 200, headers: { 'Content-Type': 'application/json' } }
+ { status: 200, headers: { 'Content-Type': 'application/json' } },
);
}
return originalFetch(input, init);
- })
+ }),
);
process.env.OPENAI_API_KEY = 'test-key';
diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts
index d8a1104..b20ed37 100644
--- a/packages/server/src/app.ts
+++ b/packages/server/src/app.ts
@@ -1,17 +1,17 @@
+import { randomUUID, timingSafeEqual } from 'node:crypto';
import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
import { URL } from 'node:url';
-import { randomUUID, timingSafeEqual } from 'node:crypto';
import {
CacheEngine,
- InMemoryAdapter,
- OpenAIEmbedder,
type CacheOptions,
type EmbeddingProvider,
+ InMemoryAdapter,
+ OpenAIEmbedder,
} from '@reaatech/llm-cache';
-import { RedisAdapter } from '@reaatech/llm-cache-adapters-redis';
import { DynamoDBAdapter } from '@reaatech/llm-cache-adapters-dynamodb';
import { QdrantAdapter } from '@reaatech/llm-cache-adapters-qdrant';
-import { MetricsCollector, Logger } from '@reaatech/llm-cache-observability';
+import { RedisAdapter } from '@reaatech/llm-cache-adapters-redis';
+import { Logger, MetricsCollector } from '@reaatech/llm-cache-observability';
import { loadConfig } from './config.js';
const config = loadConfig();
@@ -47,7 +47,7 @@ async function createStorageAdapter() {
case 'dynamodb': {
if (!config.dynamodbRegion || !config.dynamodbTable) {
throw new Error(
- 'DYNAMODB_REGION and DYNAMODB_TABLE are required when STORAGE_ADAPTER=dynamodb'
+ 'DYNAMODB_REGION and DYNAMODB_TABLE are required when STORAGE_ADAPTER=dynamodb',
);
}
return new DynamoDBAdapter({
@@ -56,7 +56,6 @@ async function createStorageAdapter() {
endpoint: config.dynamodbEndpoint,
});
}
- case 'memory':
default:
logger.info('Using in-memory storage');
return new InMemoryAdapter();
@@ -82,7 +81,6 @@ async function createVectorStorageAdapter() {
});
return adapter;
}
- case 'memory':
default:
logger.info('Using in-memory vector storage');
return new InMemoryAdapter();
@@ -105,12 +103,12 @@ function createEmbedder(): EmbeddingProvider {
return {
embed: () => {
return Promise.reject(
- new Error('OpenAI API key not configured. Set OPENAI_API_KEY environment variable.')
+ new Error('OpenAI API key not configured. Set OPENAI_API_KEY environment variable.'),
);
},
embedBatch: () => {
return Promise.reject(
- new Error('OpenAI API key not configured. Set OPENAI_API_KEY environment variable.')
+ new Error('OpenAI API key not configured. Set OPENAI_API_KEY environment variable.'),
);
},
};
@@ -303,7 +301,7 @@ export async function createApp(): Promise {
const raw = body.criteria ?? {};
const olderThanDate = raw.olderThan ? new Date(raw.olderThan) : undefined;
- if (raw.olderThan && isNaN(olderThanDate!.getTime())) {
+ if (raw.olderThan && Number.isNaN(olderThanDate?.getTime())) {
sendJson(res, 400, { error: 'Invalid "olderThan" date' });
return;
}
@@ -365,10 +363,7 @@ export async function createApp(): Promise {
if ('disconnect' in storage && typeof storage.disconnect === 'function') {
await storage.disconnect();
}
- if (
- 'disconnect' in vectorStorage &&
- typeof vectorStorage.disconnect === 'function'
- ) {
+ if ('disconnect' in vectorStorage && typeof vectorStorage.disconnect === 'function') {
await vectorStorage.disconnect();
}
}
@@ -379,7 +374,7 @@ export async function createApp(): Promise {
class HttpError extends Error {
constructor(
public status: number,
- message: string
+ message: string,
) {
super(message);
this.name = 'HttpError';
diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts
index 16a5499..36a5a57 100644
--- a/packages/server/src/config.ts
+++ b/packages/server/src/config.ts
@@ -1,4 +1,4 @@
-import { CacheConfigSchema, type CacheConfig } from '@reaatech/llm-cache';
+import { type CacheConfig, CacheConfigSchema } from '@reaatech/llm-cache';
export interface ServerConfig {
port: number;
@@ -21,13 +21,13 @@ export interface ServerConfig {
function parseNumber(value: string | undefined, defaultValue: number): number {
if (!value) return defaultValue;
const parsed = parseInt(value, 10);
- return isNaN(parsed) ? defaultValue : parsed;
+ return Number.isNaN(parsed) ? defaultValue : parsed;
}
function parseFloatValue(value: string | undefined, defaultValue: number): number {
if (!value) return defaultValue;
const parsed = parseFloat(value);
- return isNaN(parsed) ? defaultValue : parsed;
+ return Number.isNaN(parsed) ? defaultValue : parsed;
}
export function loadConfig(): ServerConfig {
@@ -80,7 +80,7 @@ export function loadConfig(): ServerConfig {
const issues = result.error.issues
.map(
(issue: { path: (string | number)[]; message: string }) =>
- `${issue.path.join('.')}: ${issue.message}`
+ `${issue.path.join('.')}: ${issue.message}`,
)
.join(', ');
throw new Error(`Invalid configuration: ${issues}`);
diff --git a/packages/server/src/index.test.ts b/packages/server/src/index.test.ts
index 0050ec6..3ca8d27 100644
--- a/packages/server/src/index.test.ts
+++ b/packages/server/src/index.test.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect } from 'vitest';
+import { describe, expect, it } from 'vitest';
import * as server from './index.js';
describe('Server exports', () => {
From c99fc77f5f6f464eea7451eb83ec01be71b5bc7d Mon Sep 17 00:00:00 2001
From: reaatech <138725666+reaatech@users.noreply.github.com>
Date: Thu, 30 Apr 2026 05:54:22 -0700
Subject: [PATCH 05/11] fix: revert biome.json to v1 format for CI
compatibility
CI installs @biomejs/biome ^1.9.4 which uses the v1 configuration schema.
The v2 schema (auto-migrated by local npx biome 2.4.13) is incompatible.
---
biome.json | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/biome.json b/biome.json
index e48a70d..d398bd8 100644
--- a/biome.json
+++ b/biome.json
@@ -1,9 +1,11 @@
{
- "$schema": "https://biomejs.dev/schemas/2.4.13/schema.json",
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"files": {
- "includes": ["**", "!**/dist", "!**/node_modules", "!**/coverage", "!**/.turbo"]
+ "ignore": ["dist", "node_modules", "coverage"]
+ },
+ "organizeImports": {
+ "enabled": true
},
- "assist": { "actions": { "source": { "organizeImports": "on" } } },
"linter": {
"enabled": true,
"rules": {
From f13483c1b38513e1229f9edc31588592df3a53d5 Mon Sep 17 00:00:00 2001
From: reaatech <138725666+reaatech@users.noreply.github.com>
Date: Thu, 30 Apr 2026 05:56:03 -0700
Subject: [PATCH 06/11] fix: re-format with biome 1.9.4 for CI version parity
---
packages/adapters/dynamodb/package.json | 13 ++-----------
packages/adapters/qdrant/package.json | 13 ++-----------
packages/adapters/redis/package.json | 12 ++----------
packages/adapters/redis/src/RedisAdapter.ts | 4 ++--
packages/core/package.json | 14 ++------------
packages/cost-tracker/package.json | 13 ++-----------
packages/observability/package.json | 13 ++-----------
packages/server/package.json | 12 ++----------
packages/server/src/app.test.ts | 2 +-
packages/server/src/app.ts | 2 +-
packages/server/src/config.ts | 4 ++--
11 files changed, 20 insertions(+), 82 deletions(-)
diff --git a/packages/adapters/dynamodb/package.json b/packages/adapters/dynamodb/package.json
index e944edb..7cf5bce 100644
--- a/packages/adapters/dynamodb/package.json
+++ b/packages/adapters/dynamodb/package.json
@@ -20,17 +20,8 @@
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
- "keywords": [
- "llm-cache",
- "dynamodb",
- "aws",
- "cache",
- "adapter"
- ],
- "files": [
- "dist",
- "README.md"
- ],
+ "keywords": ["llm-cache", "dynamodb", "aws", "cache", "adapter"],
+ "files": ["dist", "README.md"],
"publishConfig": {
"access": "public"
},
diff --git a/packages/adapters/qdrant/package.json b/packages/adapters/qdrant/package.json
index f250a89..844db7c 100644
--- a/packages/adapters/qdrant/package.json
+++ b/packages/adapters/qdrant/package.json
@@ -20,17 +20,8 @@
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
- "keywords": [
- "llm-cache",
- "qdrant",
- "vector",
- "semantic-search",
- "adapter"
- ],
- "files": [
- "dist",
- "README.md"
- ],
+ "keywords": ["llm-cache", "qdrant", "vector", "semantic-search", "adapter"],
+ "files": ["dist", "README.md"],
"publishConfig": {
"access": "public"
},
diff --git a/packages/adapters/redis/package.json b/packages/adapters/redis/package.json
index a040905..43e56f7 100644
--- a/packages/adapters/redis/package.json
+++ b/packages/adapters/redis/package.json
@@ -20,16 +20,8 @@
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
- "keywords": [
- "llm-cache",
- "redis",
- "cache",
- "adapter"
- ],
- "files": [
- "dist",
- "README.md"
- ],
+ "keywords": ["llm-cache", "redis", "cache", "adapter"],
+ "files": ["dist", "README.md"],
"publishConfig": {
"access": "public"
},
diff --git a/packages/adapters/redis/src/RedisAdapter.ts b/packages/adapters/redis/src/RedisAdapter.ts
index 0472913..043186d 100644
--- a/packages/adapters/redis/src/RedisAdapter.ts
+++ b/packages/adapters/redis/src/RedisAdapter.ts
@@ -5,7 +5,7 @@ import type {
StorageAdapter,
StorageStats,
} from '@reaatech/llm-cache';
-import { createClient, type RedisClientType } from 'redis';
+import { type RedisClientType, createClient } from 'redis';
export interface RedisAdapterConfig {
url: string;
@@ -236,7 +236,7 @@ export class RedisAdapter implements StorageAdapter {
try {
const info = await this.client.info('keyspace');
const match = info?.match(/keys=(\d+)/);
- totalEntries = match ? parseInt(match[1], 10) : 0;
+ totalEntries = match ? Number.parseInt(match[1], 10) : 0;
} catch {
// info('keyspace') may not be available on all Redis versions
}
diff --git a/packages/core/package.json b/packages/core/package.json
index 658c958..5a15e46 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -20,18 +20,8 @@
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
- "keywords": [
- "llm",
- "cache",
- "semantic-cache",
- "embeddings",
- "openai",
- "vector-search"
- ],
- "files": [
- "dist",
- "README.md"
- ],
+ "keywords": ["llm", "cache", "semantic-cache", "embeddings", "openai", "vector-search"],
+ "files": ["dist", "README.md"],
"publishConfig": {
"access": "public"
},
diff --git a/packages/cost-tracker/package.json b/packages/cost-tracker/package.json
index cfb5bf6..517ce28 100644
--- a/packages/cost-tracker/package.json
+++ b/packages/cost-tracker/package.json
@@ -20,17 +20,8 @@
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
- "keywords": [
- "llm-cache",
- "cost",
- "pricing",
- "openai",
- "anthropic"
- ],
- "files": [
- "dist",
- "README.md"
- ],
+ "keywords": ["llm-cache", "cost", "pricing", "openai", "anthropic"],
+ "files": ["dist", "README.md"],
"publishConfig": {
"access": "public"
},
diff --git a/packages/observability/package.json b/packages/observability/package.json
index 8994052..fef1060 100644
--- a/packages/observability/package.json
+++ b/packages/observability/package.json
@@ -20,17 +20,8 @@
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
- "keywords": [
- "llm-cache",
- "metrics",
- "logging",
- "prometheus",
- "observability"
- ],
- "files": [
- "dist",
- "README.md"
- ],
+ "keywords": ["llm-cache", "metrics", "logging", "prometheus", "observability"],
+ "files": ["dist", "README.md"],
"publishConfig": {
"access": "public"
},
diff --git a/packages/server/package.json b/packages/server/package.json
index d4b81c5..46a1aa2 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -20,19 +20,11 @@
"bugs": {
"url": "https://github.com/reaatech/llm-cache/issues"
},
- "keywords": [
- "llm-cache",
- "server",
- "http",
- "cache"
- ],
+ "keywords": ["llm-cache", "server", "http", "cache"],
"bin": {
"llm-cache-server": "./dist/cli.js"
},
- "files": [
- "dist",
- "README.md"
- ],
+ "files": ["dist", "README.md"],
"publishConfig": {
"access": "public"
},
diff --git a/packages/server/src/app.test.ts b/packages/server/src/app.test.ts
index ac6dbc2..1bbf67f 100644
--- a/packages/server/src/app.test.ts
+++ b/packages/server/src/app.test.ts
@@ -40,7 +40,7 @@ describe('Server App', () => {
afterAll(async () => {
await new Promise((resolve) => app.server.close(() => resolve()));
vi.unstubAllGlobals();
- delete process.env.OPENAI_API_KEY;
+ process.env.OPENAI_API_KEY = undefined;
});
async function fetchJson(path: string, opts?: RequestInit) {
diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts
index b20ed37..f183f81 100644
--- a/packages/server/src/app.ts
+++ b/packages/server/src/app.ts
@@ -1,5 +1,5 @@
import { randomUUID, timingSafeEqual } from 'node:crypto';
-import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
+import { type IncomingMessage, type ServerResponse, createServer } from 'node:http';
import { URL } from 'node:url';
import {
CacheEngine,
diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts
index 36a5a57..9550808 100644
--- a/packages/server/src/config.ts
+++ b/packages/server/src/config.ts
@@ -20,13 +20,13 @@ export interface ServerConfig {
function parseNumber(value: string | undefined, defaultValue: number): number {
if (!value) return defaultValue;
- const parsed = parseInt(value, 10);
+ const parsed = Number.parseInt(value, 10);
return Number.isNaN(parsed) ? defaultValue : parsed;
}
function parseFloatValue(value: string | undefined, defaultValue: number): number {
if (!value) return defaultValue;
- const parsed = parseFloat(value);
+ const parsed = Number.parseFloat(value);
return Number.isNaN(parsed) ? defaultValue : parsed;
}
From c2dca1322d127eb451a08b3e7ee1b2621fe7d9b3 Mon Sep 17 00:00:00 2001
From: reaatech <138725666+reaatech@users.noreply.github.com>
Date: Thu, 30 Apr 2026 05:58:06 -0700
Subject: [PATCH 07/11] fix: type fetchJson return in app.test.ts
The tsconfig.typecheck.json path aliases now catch unknown-type errors
from JSON.parse that were previously hidden by per-package typecheck.
---
packages/server/src/app.test.ts | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/packages/server/src/app.test.ts b/packages/server/src/app.test.ts
index 1bbf67f..72374ce 100644
--- a/packages/server/src/app.test.ts
+++ b/packages/server/src/app.test.ts
@@ -43,9 +43,12 @@ describe('Server App', () => {
process.env.OPENAI_API_KEY = undefined;
});
- async function fetchJson(path: string, opts?: RequestInit) {
+ async function fetchJson(
+ path: string,
+ opts?: RequestInit,
+ ): Promise<{ status: number; data: Record }> {
const res = await fetch(`${baseUrl}${path}`, opts);
- const data = await res.json();
+ const data = (await res.json()) as Record;
return { status: res.status, data };
}
From 4a443cf621cf5728bc16b6893e468f88d5c581f4 Mon Sep 17 00:00:00 2001
From: reaatech <138725666+reaatech@users.noreply.github.com>
Date: Thu, 30 Apr 2026 05:58:47 -0700
Subject: [PATCH 08/11] fix: use any type in test fetchJson helper
Record still yields unknown on property access.
Use any with biome-ignore since it's a test response fixture.
---
packages/server/src/app.test.ts | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/packages/server/src/app.test.ts b/packages/server/src/app.test.ts
index 72374ce..cc0e7a5 100644
--- a/packages/server/src/app.test.ts
+++ b/packages/server/src/app.test.ts
@@ -43,12 +43,13 @@ describe('Server App', () => {
process.env.OPENAI_API_KEY = undefined;
});
+ // biome-ignore lint/suspicious/noExplicitAny: test response fixture
async function fetchJson(
path: string,
opts?: RequestInit,
- ): Promise<{ status: number; data: Record }> {
+ ): Promise<{ status: number; data: any }> {
const res = await fetch(`${baseUrl}${path}`, opts);
- const data = (await res.json()) as Record;
+ const data = await res.json();
return { status: res.status, data };
}
From 1357261376d0e229e5bfc0ad4cd0cd3e6dbbd8f3 Mon Sep 17 00:00:00 2001
From: reaatech <138725666+reaatech@users.noreply.github.com>
Date: Thu, 30 Apr 2026 06:01:34 -0700
Subject: [PATCH 09/11] fix: move biome-ignore closer to any type annotation
---
packages/server/src/app.test.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/server/src/app.test.ts b/packages/server/src/app.test.ts
index cc0e7a5..7d4ab46 100644
--- a/packages/server/src/app.test.ts
+++ b/packages/server/src/app.test.ts
@@ -43,10 +43,10 @@ describe('Server App', () => {
process.env.OPENAI_API_KEY = undefined;
});
- // biome-ignore lint/suspicious/noExplicitAny: test response fixture
async function fetchJson(
path: string,
opts?: RequestInit,
+ // biome-ignore lint/suspicious/noExplicitAny: test response fixture
): Promise<{ status: number; data: any }> {
const res = await fetch(`${baseUrl}${path}`, opts);
const data = await res.json();
From eb514920514032fc1977e12322b5012a9490209c Mon Sep 17 00:00:00 2001
From: reaatech <138725666+reaatech@users.noreply.github.com>
Date: Thu, 30 Apr 2026 06:04:31 -0700
Subject: [PATCH 10/11] fix(ci): run pnpm build before coverage for dist
resolution
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Packages with self-imports resolve via exports→dist/ which needs tsup output.
---
.github/workflows/ci.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ec01e57..69f8591 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -112,6 +112,9 @@ jobs:
- name: Install dependencies
run: pnpm install
+ - name: Build packages
+ run: pnpm build
+
- name: Run coverage (core)
run: pnpm --filter @reaatech/llm-cache test:coverage
From 9fbef050e72f5aa020dd5687a69c1f57aeb86137 Mon Sep 17 00:00:00 2001
From: reaatech <138725666+reaatech@users.noreply.github.com>
Date: Thu, 30 Apr 2026 06:14:53 -0700
Subject: [PATCH 11/11] fix(ci): remove skipped deploy/publish jobs from CI
workflow
deploy-staging, deploy-production, and publish jobs used job-level if:
conditions that skip on PR branches, cluttering the checks list with
skipped statuses. These belong in release.yml, not ci.yml.
---
.github/workflows/ci.yml | 54 ----------------------------------------
1 file changed, 54 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 69f8591..27b7894 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -146,57 +146,3 @@ jobs:
- name: Build all packages
run: pnpm build
-
- deploy-staging:
- name: Deploy to Staging
- needs: [build, coverage]
- if: github.ref == 'refs/heads/develop'
- runs-on: ubuntu-latest
- environment: staging
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- - name: Deploy
- run: echo "Deploy to staging would run here"
-
- deploy-production:
- name: Deploy to Production
- needs: [build, coverage]
- if: github.ref == 'refs/heads/main'
- runs-on: ubuntu-latest
- environment: production
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- - name: Deploy
- run: echo "Deploy to production would run here"
-
- publish:
- name: Publish to npm
- needs: [build, coverage]
- if: startsWith(github.ref, 'refs/tags/v')
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Setup pnpm
- uses: pnpm/action-setup@v4
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version: ${{ env.NODE_VERSION }}
- registry-url: 'https://registry.npmjs.org'
- cache: 'pnpm'
-
- - name: Install dependencies
- run: pnpm install
-
- - name: Build all packages
- run: pnpm build
-
- - name: Publish packages
- run: pnpm -r publish --access public --no-git-checks
- env:
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}