Skip to content

Commit a44a202

Browse files
committed
pine-gate v0.1
Signed-off-by: amanycodes <[email protected]>
1 parent 3cfa3d2 commit a44a202

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+4035
-105
lines changed

.github/workflows/release.yml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
env:
9+
REGISTRY: ghcr.io
10+
IMAGE_NAME: ${{ github.repository_owner }}/pine-gate
11+
12+
jobs:
13+
docker-and-chart:
14+
runs-on: ubuntu-latest
15+
permissions:
16+
contents: write
17+
packages: write
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
22+
- name: Set up Go
23+
uses: actions/setup-go@v5
24+
with:
25+
go-version: '1.23.x'
26+
27+
- name: Extract version
28+
id: ver
29+
run: |
30+
RAW_TAG="${GITHUB_REF_NAME}"
31+
# strip leading v if present
32+
VERSION="${RAW_TAG#v}"
33+
echo "version=${VERSION}" >> $GITHUB_OUTPUT
34+
35+
- name: Log in to GHCR
36+
uses: docker/login-action@v3
37+
with:
38+
registry: ${{ env.REGISTRY }}
39+
username: ${{ github.repository_owner }}
40+
password: ${{ secrets.GITHUB_TOKEN }}
41+
42+
- name: Set up QEMU
43+
uses: docker/setup-qemu-action@v3
44+
45+
- name: Set up Docker Buildx
46+
uses: docker/setup-buildx-action@v3
47+
48+
- name: Build and push image
49+
uses: docker/build-push-action@v6
50+
with:
51+
context: .
52+
file: deployments/docker/Dockerfile
53+
platforms: linux/amd64,linux/arm64
54+
push: true
55+
build-args: |
56+
VERSION=${{ steps.ver.outputs.version }}
57+
tags: |
58+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.ver.outputs.version }}
59+
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
60+
61+
- name: Set up Helm
62+
uses: azure/setup-helm@v4
63+
64+
- name: Package Helm chart
65+
run: |
66+
CHART_VER=${{ steps.ver.outputs.version }}
67+
sed -i "s/^version:.*/version: ${CHART_VER}/" charts/pine-gate/Chart.yaml
68+
sed -i "s/^appVersion:.*/appVersion: \"${CHART_VER}\"/" charts/pine-gate/Chart.yaml
69+
helm package charts/pine-gate --version ${CHART_VER} --app-version ${CHART_VER} -d dist
70+
71+
- name: Create GitHub Release
72+
uses: softprops/action-gh-release@v2
73+
with:
74+
tag_name: ${{ github.ref_name }}
75+
files: |
76+
dist/pine-gate-${{ steps.ver.outputs.version }}.tgz
77+
env:
78+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
79+

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@ bin/
33
*.exe
44
*.out
55
*.test
6+
dist/
7+
coverage/
8+
*.coverprofile
9+
*.cov
10+
11+
# CLI completions/scripts
12+
_pinectl
13+
pinectl.bash
14+
15+
# Env files
16+
.env
17+
.env.*
618

719
# Build cache
820
*.log
@@ -21,5 +33,9 @@ Thumbs.db
2133
.vscode/
2234
*.iml
2335

36+
# Go workspace files (optional)
37+
go.work
38+
go.work.sum
39+
2440
# Docker
2541
*.tar

Makefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ APP=pine-gate
22
PKG=./...
33
IMG?=amanycodes/pine-gate:dev
44

5-
.PHONY: build run test docker kind
5+
.PHONY: build run test docker kind pinectl pinectl-install-completions
66

77
build:
88
go build -o bin/$(APP) ./cmd/gateway
@@ -19,3 +19,10 @@ docker:
1919
kind:
2020
kind create cluster --name aigw || true
2121
kubectl apply -f deployments/k8s/
22+
23+
pinectl:
24+
go build -o bin/pinectl ./cmd/pinectl
25+
26+
pinectl-install-completions: pinectl
27+
./bin/pinectl completions bash > pinectl.bash || true
28+
./bin/pinectl completions zsh > _pinectl || true

README.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
p:ine-gate
2+
3+
Open-source, K8s-deployable AI Gateway for local & remote LLMs with quotas, streaming, routing, and observability.
4+
5+
## Features
6+
- Backends: Echo, Ollama (local), vLLM (OpenAI-compatible), OpenRouter, OpenAI, Anthropic
7+
- Routing: static rules + canary weighted splits
8+
- SSE streaming end-to-end
9+
- Rate limiting per API key (in-mem or Redis)
10+
- Usage counters and admin endpoint
11+
- Prometheus metrics with backend labels
12+
- OTel tracing (OTLP exporter)
13+
- Docker + Helm (ServiceMonitor + HPA)
14+
15+
## Quickstart (Local)
16+
```
17+
cp .env.example .env # optional, or create your own
18+
CONFIG_FILE=./configs/config.example.yaml make run
19+
curl -i http://localhost:8080/healthz
20+
curl -s -H 'x-api-key: dev-key' -H 'Content-Type: application/json' -X POST http://localhost:8080/v1/completions -d '{"model":"echo","prompt":"hello"}'
21+
```
22+
23+
### Enable OpenRouter
24+
```
25+
PINE_GATE_BACKENDS_OPENROUTER_ENABLED=true \
26+
PINE_GATE_BACKENDS_OPENROUTER_APIKEY=<YOUR_KEY> \
27+
CONFIG_FILE=./configs/config.example.yaml make run
28+
curl -s -H 'x-api-key: dev-key' -H 'Content-Type: application/json' -X POST \
29+
http://localhost:8080/v1/completions -d '{"model":"openrouter:mistralai/mistral-7b-instruct:free","prompt":"hello"}'
30+
```
31+
32+
### Enable Ollama (local)
33+
```
34+
PINE_GATE_BACKENDS_OLLAMA_ENABLED=true \
35+
PINE_GATE_BACKENDS_OLLAMA_HOST=http://localhost:11434 \
36+
CONFIG_FILE=./configs/config.example.yaml make run
37+
curl -s -H 'x-api-key: dev-key' -H 'Content-Type: application/json' -X POST \
38+
http://localhost:8080/v1/completions -d '{"model":"ollama:llama3","prompt":"hello"}'
39+
```
40+
41+
### vLLM (OpenAI-compatible)
42+
```
43+
PINE_GATE_BACKENDS_VLLM_ENABLED=true \
44+
PINE_GATE_BACKENDS_VLLM_BASE_URL=http://localhost:8000/v1 \
45+
CONFIG_FILE=./configs/config.example.yaml make run
46+
```
47+
48+
### OpenAI
49+
```
50+
PINE_GATE_BACKENDS_OPENAI_ENABLED=true \
51+
PINE_GATE_BACKENDS_OPENAI_APIKEY=<OPENAI_KEY> \
52+
CONFIG_FILE=./configs/config.example.yaml make run
53+
```
54+
55+
### Anthropic
56+
```
57+
PINE_GATE_BACKENDS_ANTHROPIC_ENABLED=true \
58+
PINE_GATE_BACKENDS_ANTHROPIC_APIKEY=<ANTHROPIC_KEY> \
59+
CONFIG_FILE=./configs/config.example.yaml make run
60+
```
61+
62+
## Redis Rate Limit + Usage
63+
```
64+
docker run --rm -p 6379:6379 redis:7
65+
PINE_GATE_REDIS_ENABLED=true PINE_GATE_REDIS_ADDR=localhost:6379 \
66+
PINE_GATE_AUTH_ADMIN_KEY=admin CONFIG_FILE=./configs/config.example.yaml make run
67+
curl -s -H 'x-admin-key: admin' 'http://localhost:8080/v1/usage?key=dev-key'
68+
```
69+
70+
## Tracing (Jaeger all-in-one)
71+
```
72+
docker run --rm -p 16686:16686 -p 4318:4318 -e COLLECTOR_OTLP_ENABLED=true jaegertracing/all-in-one:1.57
73+
OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4318 CONFIG_FILE=./configs/config.example.yaml make run
74+
75+
## .env Support
76+
- pine-gate automatically loads a `.env` file from the working directory.
77+
- Put any configuration env vars in `.env` instead of prefixing commands, for example:
78+
79+
```
80+
# .env
81+
PINE_GATE_BACKENDS_OLLAMA_ENABLED=true
82+
PINE_GATE_BACKENDS_OLLAMA_HOST=http://localhost:11434
83+
PINE_GATE_BACKENDS_OPENROUTER_ENABLED=false
84+
PINE_GATE_BACKENDS_OPENROUTER_APIKEY=
85+
PINE_GATE_BACKENDS_OPENAI_ENABLED=false
86+
PINE_GATE_BACKENDS_VLLM_ENABLED=false
87+
OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4318
88+
PINE_GATE_LIMITS_RATE_RPS=5
89+
PINE_GATE_LIMITS_BURST=10
90+
```
91+
92+
With `.env` present, simply run:
93+
94+
```
95+
make run
96+
# or
97+
./bin/pinectl serve --config ./configs/config.example.yaml
98+
```
99+
```
100+
101+
## Helm Install
102+
```
103+
helm install pine-gate charts/pine-gate --set auth.apiKey=dev-key
104+
# Optional extras:
105+
# --set backends.openrouter.enabled=true --set backends.openrouter.apiKey=$OPENROUTER_API_KEY
106+
# --set redis.enabled=true --set auth.adminKey=admin
107+
# --set monitoring.enabled=true --set otel.enabled=true
108+
kubectl port-forward deploy/pine-gate 8080:8080
109+
```
110+
111+
## Security
112+
- Container: distroless non-root, read-only FS, dropped capabilities, seccomp RuntimeDefault
113+
- K8s: security contexts set via Helm values
114+
115+
## See Also
116+
- docs/RUNBOOK.md
117+
- docs/SLOs.md

charts/pine-gate/Chart.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiVersion: v2
2+
name: pine-gate
3+
description: AI Gateway for local & remote LLMs with quotas, streaming, routing, and observability
4+
type: application
5+
version: 0.1.0
6+
appVersion: "0.1.0"
7+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
1. Get the application URL by running these commands:
2+
3+
export POD=$(kubectl get pods -l app={{ include "pine-gate.fullname" . }} -o jsonpath="{.items[0].metadata.name}")
4+
kubectl port-forward $POD 8080:8080
5+
echo "Health: http://localhost:8080/healthz"
6+
echo "Metrics: http://localhost:8080/metrics"
7+
8+
2. Configure API key
9+
10+
Your API key is set from values at install time. Update the Secret if needed:
11+
kubectl edit secret {{ include "pine-gate.fullname" . }}-secret
12+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{{- define "pine-gate.name" -}}
2+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
3+
{{- end -}}
4+
5+
{{- define "pine-gate.fullname" -}}
6+
{{- if .Values.fullnameOverride -}}
7+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
8+
{{- else -}}
9+
{{- $name := default .Chart.Name .Values.nameOverride -}}
10+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
11+
{{- end -}}
12+
{{- end -}}
13+
14+
{{- define "pine-gate.labels" -}}
15+
app.kubernetes.io/name: {{ include "pine-gate.name" . }}
16+
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
17+
app.kubernetes.io/instance: {{ .Release.Name }}
18+
app.kubernetes.io/managed-by: {{ .Release.Service }}
19+
{{- end -}}
20+

0 commit comments

Comments
 (0)