Skip to content

Commit 77ad98a

Browse files
[plugins] feat: redis caching plugin added
1 parent 8b88c85 commit 77ad98a

File tree

5 files changed

+679
-0
lines changed

5 files changed

+679
-0
lines changed

plugins/redis/README.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# Redis Cache Plugin for Bifrost
2+
3+
This plugin provides Redis-based caching functionality for Bifrost requests. It caches responses based on request body hashes and returns cached responses for identical requests, significantly improving performance and reducing API costs.
4+
5+
## Features
6+
7+
- **Request Body Hashing**: Uses SHA256 to generate consistent hashes from request bodies
8+
- **Response Caching**: Stores complete responses in Redis with configurable TTL
9+
- **Cache Hit Detection**: Returns cached responses for identical requests
10+
- **Cache Management**: Provides utilities to clear cache and get cache information
11+
- **Configurable**: Supports custom Redis connection settings, TTL, and key prefixes
12+
13+
## Installation
14+
15+
```bash
16+
go get github.com/redis/go-redis/v9
17+
go get github.com/maximhq/bifrost/core
18+
```
19+
20+
## Usage
21+
22+
### Basic Setup
23+
24+
```go
25+
import (
26+
"github.com/maximhq/bifrost/plugins/redis"
27+
"time"
28+
)
29+
30+
// Configure the Redis plugin
31+
config := redis.RedisPluginConfig{
32+
RedisAddr: "localhost:6379", // Redis server address
33+
RedisPassword: "", // Redis password (if required)
34+
RedisDB: 0, // Redis database number
35+
TTL: 24 * time.Hour, // Cache TTL (24 hours)
36+
Prefix: "bifrost:cache:", // Cache key prefix
37+
}
38+
39+
// Create the plugin
40+
plugin, err := redis.NewRedisPlugin(config)
41+
if err != nil {
42+
log.Fatal("Failed to create Redis plugin:", err)
43+
}
44+
45+
// Use with Bifrost
46+
bifrostConfig := schemas.BifrostConfig{
47+
Account: yourAccount,
48+
Plugins: []schemas.Plugin{plugin},
49+
// ... other config
50+
}
51+
```
52+
53+
### Configuration Options
54+
55+
| Option | Type | Default | Description |
56+
| --------------- | --------------- | ------------------ | --------------------------------- |
57+
| `RedisAddr` | `string` | `"localhost:6379"` | Redis server address |
58+
| `RedisPassword` | `string` | `""` | Redis password (empty if no auth) |
59+
| `RedisDB` | `int` | `0` | Redis database number |
60+
| `TTL` | `time.Duration` | `24 * time.Hour` | Time-to-live for cached responses |
61+
| `Prefix` | `string` | `"bifrost:cache:"` | Prefix for cache keys |
62+
63+
### Advanced Usage
64+
65+
#### Cache Management
66+
67+
```go
68+
// Get cache information
69+
info, err := plugin.(*redis.Plugin).GetCacheInfo(ctx)
70+
if err != nil {
71+
log.Printf("Cache info: %+v", info)
72+
}
73+
74+
// Clear all cached entries
75+
err = plugin.(*redis.Plugin).ClearCache(ctx)
76+
if err != nil {
77+
log.Printf("Failed to clear cache: %v", err)
78+
}
79+
80+
// Close Redis connection when done
81+
defer plugin.(*redis.Plugin).Close()
82+
```
83+
84+
## How It Works
85+
86+
### Request Hashing
87+
88+
The plugin generates a SHA256 hash of the normalized request including:
89+
90+
- Provider
91+
- Model
92+
- Input (chat completion or text completion)
93+
- Parameters
94+
- Fallbacks
95+
96+
Identical requests will always produce the same hash, enabling effective caching.
97+
98+
### Caching Flow
99+
100+
1. **PreHook**:
101+
102+
- Generates hash from incoming request
103+
- Checks Redis for cached response
104+
- If found, returns cached response (skips provider call)
105+
- If not found, stores hash in context for PostHook
106+
107+
2. **PostHook**:
108+
- Retrieves hash from context
109+
- Stores the response in Redis with the hash as key
110+
- Sets TTL on the cached entry
111+
112+
### Cache Keys
113+
114+
Cache keys follow the pattern: `{prefix}{sha256_hash}`
115+
116+
Example: `bifrost:cache:a1b2c3d4e5f6...`
117+
118+
## Testing
119+
120+
Run the tests with a Redis instance running:
121+
122+
```bash
123+
# Start Redis (using Docker)
124+
docker run -d -p 6379:6379 redis:latest
125+
126+
# Run tests
127+
go test ./...
128+
```
129+
130+
Tests will be skipped if Redis is not available.
131+
132+
## Performance Benefits
133+
134+
- **Reduced API Calls**: Identical requests are served from cache
135+
- **Lower Latency**: Cache hits return immediately without network calls
136+
- **Cost Savings**: Fewer API calls to expensive LLM providers
137+
- **Improved Reliability**: Cached responses available even if provider is down
138+
139+
## Error Handling
140+
141+
The plugin is designed to fail gracefully:
142+
143+
- If Redis is unavailable, requests continue without caching
144+
- If cache retrieval fails, requests proceed normally
145+
- If cache storage fails, responses are returned without caching
146+
- Malformed cached data is ignored and requests proceed normally
147+
148+
## Redis Connection
149+
150+
The plugin supports standard Redis connection options:
151+
152+
- Single Redis instance
153+
- Redis with authentication
154+
- Different Redis databases
155+
- Custom connection timeouts (5-second timeout for initial connection)
156+
157+
## Cache Invalidation
158+
159+
- **TTL-based**: Entries automatically expire after the configured TTL
160+
- **Manual**: Use `ClearCache()` to remove all entries
161+
- **Selective**: Redis CLI can be used for manual key management
162+
163+
## Best Practices
164+
165+
1. **Set appropriate TTL**: Balance between cache efficiency and data freshness
166+
2. **Use meaningful prefixes**: Helps organize cache keys in shared Redis instances
167+
3. **Monitor cache hit rates**: Use `GetCacheInfo()` to track cache effectiveness
168+
4. **Consider cache size**: Monitor Redis memory usage in production
169+
5. **Handle Redis failures**: Ensure your application works without caching
170+
171+
## Security Considerations
172+
173+
- **Sensitive Data**: Be cautious about caching responses containing sensitive information
174+
- **Redis Security**: Use password authentication and network security for Redis
175+
- **Key Collisions**: The SHA256 hash makes collisions extremely unlikely
176+
- **Data Isolation**: Use different Redis databases or prefixes for different environments

plugins/redis/go.mod

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
module github.com/maximhq/bifrost/plugins/redis
2+
3+
go 1.24.1
4+
5+
require (
6+
github.com/maximhq/bifrost/core v1.0.9
7+
github.com/redis/go-redis/v9 v9.10.0
8+
)
9+
10+
require (
11+
cloud.google.com/go/compute/metadata v0.3.0 // indirect
12+
github.com/andybalholm/brotli v1.1.1 // indirect
13+
github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
14+
github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect
15+
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect
16+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
17+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
18+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
19+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
20+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
21+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
22+
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect
23+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
24+
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect
25+
github.com/aws/smithy-go v1.22.3 // indirect
26+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
27+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
28+
github.com/goccy/go-json v0.10.5 // indirect
29+
github.com/klauspost/compress v1.18.0 // indirect
30+
github.com/valyala/bytebufferpool v1.0.0 // indirect
31+
github.com/valyala/fasthttp v1.60.0 // indirect
32+
golang.org/x/net v0.39.0 // indirect
33+
golang.org/x/oauth2 v0.30.0 // indirect
34+
golang.org/x/text v0.24.0 // indirect
35+
)

plugins/redis/go.sum

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
2+
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
3+
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
4+
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
5+
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
6+
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
7+
github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM=
8+
github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g=
9+
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM=
10+
github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ=
11+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
12+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
13+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
14+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
15+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
16+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
17+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
18+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
19+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
20+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
21+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
22+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
23+
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8=
24+
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
25+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako=
26+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
27+
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY=
28+
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
29+
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
30+
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
31+
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
32+
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
33+
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
34+
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
35+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
36+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
37+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
38+
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
39+
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
40+
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
41+
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
42+
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
43+
github.com/maximhq/bifrost/core v1.0.9 h1:OWUHCWCQsBH43YPIy2AsqNMZhoFXYe/qhJSCLbw5su8=
44+
github.com/maximhq/bifrost/core v1.0.9/go.mod h1:8ycaWQ9bjQezoUT/x6a82VmPjoqLzyGglQ0RnnlZjqo=
45+
github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs=
46+
github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
47+
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
48+
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
49+
github.com/valyala/fasthttp v1.60.0 h1:kBRYS0lOhVJ6V+bYN8PqAHELKHtXqwq9zNMLKx1MBsw=
50+
github.com/valyala/fasthttp v1.60.0/go.mod h1:iY4kDgV3Gc6EqhRZ8icqcmlG6bqhcDXfuHgTO4FXCvc=
51+
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
52+
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
53+
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
54+
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
55+
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
56+
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
57+
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
58+
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=

0 commit comments

Comments
 (0)