diff --git a/config.example.yaml b/config.example.yaml index 797e639..a7b0121 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -1,11 +1,19 @@ port: 8082 # telemetry: -# endpoint: "localhost:4317" # OTLP gRPC endpoint (omit to disable export) -# insecure: true # disable TLS for local dev # service_name: "anthropic-proxy" -# headers: # optional auth headers (e.g. Grafana Cloud) -# Authorization: "Basic ..." +# export: +# endpoint: "localhost:4317" # OTLP gRPC endpoint (omit to disable export) +# insecure: true # disable TLS for local dev +# headers: # optional auth headers (e.g. Grafana Cloud) +# Authorization: "Basic ..." +# embedded: +# enabled: true # start embedded Perses dashboard + VictoriaMetrics +# port: 8080 # Perses dashboard port +# vm_port: 8428 # VictoriaMetrics listen port +# bin_dir: "" # download dir (default: ~/.cache/anthropic-proxy/bin) +# perses_binary: "" # custom path to perses binary (default: auto-download) +# vm_binary: "" # custom path to victoria-metrics binary (default: auto-download) logging: level: debug @@ -43,21 +51,21 @@ sanitize: - match: "Workspace root folder" replace: "Working directory" body: - - match: "anomalyco/opencode" + - match: "anthropics/claude-code" replace: "anthropics/claude-code" - - match: "anomalyco" + - match: "anthropic" replace: "anthropic" - - match: "oh-my-opencode" + - match: "system-directive" replace: "system-directive" - - match: "ohmyopencode" + - match: "claude-code" replace: "claude-code" - - match: "oh-my-openagent" + - match: "claude-agent" replace: "claude-agent" - - match: "omo_internal_initiator" + - match: "system_initiator" replace: "system_initiator" - - match: "call_omo_agent" + - match: "call_agent" replace: "call_agent" - - match: "opencode.ai" + - match: "claude.ai" replace: "claude.ai" - - match: "opencode" + - match: "agent" replace: "agent" diff --git a/examples/perses/dashboard.json b/examples/perses/dashboard.json new file mode 100644 index 0000000..f389f7b --- /dev/null +++ b/examples/perses/dashboard.json @@ -0,0 +1,450 @@ +{ + "kind": "Dashboard", + "metadata": { + "name": "proxy", + "createdAt": "2026-04-14T19:47:48.013238204Z", + "updatedAt": "2026-04-14T19:49:30.874125459Z", + "version": 1, + "project": "anthropic-proxy" + }, + "spec": { + "display": { + "name": "Anthropic Proxy" + }, + "datasources": { + "vm": { + "default": true, + "plugin": { + "kind": "PrometheusDatasource", + "spec": { + "directUrl": "http://localhost:9428" + } + } + } + }, + "panels": { + "latency": { + "kind": "Panel", + "spec": { + "display": { + "name": "Latency" + }, + "plugin": { + "kind": "TimeSeriesChart", + "spec": { + "legend": { + "position": "bottom" + }, + "yAxis": { + "format": { + "unit": "milliseconds" + } + } + } + }, + "queries": [ + { + "kind": "TimeSeriesQuery", + "spec": { + "plugin": { + "kind": "PrometheusTimeSeriesQuery", + "spec": { + "datasource": { + "kind": "PrometheusDatasource", + "name": "vm" + }, + "query": "histogram_quantile(0.50, rate(proxy_request_duration_ms_milliseconds_bucket[5m]))", + "seriesNameFormat": "p50" + } + } + } + }, + { + "kind": "TimeSeriesQuery", + "spec": { + "plugin": { + "kind": "PrometheusTimeSeriesQuery", + "spec": { + "datasource": { + "kind": "PrometheusDatasource", + "name": "vm" + }, + "query": "histogram_quantile(0.95, rate(proxy_request_duration_ms_milliseconds_bucket[5m]))", + "seriesNameFormat": "p95" + } + } + } + }, + { + "kind": "TimeSeriesQuery", + "spec": { + "plugin": { + "kind": "PrometheusTimeSeriesQuery", + "spec": { + "datasource": { + "kind": "PrometheusDatasource", + "name": "vm" + }, + "query": "histogram_quantile(0.99, rate(proxy_request_duration_ms_milliseconds_bucket[5m]))", + "seriesNameFormat": "p99" + } + } + } + } + ] + } + }, + "request_rate": { + "kind": "Panel", + "spec": { + "display": { + "name": "Request Rate" + }, + "plugin": { + "kind": "TimeSeriesChart", + "spec": { + "legend": { + "position": "bottom" + } + } + }, + "queries": [ + { + "kind": "TimeSeriesQuery", + "spec": { + "plugin": { + "kind": "PrometheusTimeSeriesQuery", + "spec": { + "datasource": { + "kind": "PrometheusDatasource", + "name": "vm" + }, + "query": "rate(proxy_request_count_total[5m])", + "seriesNameFormat": "req/s" + } + } + } + } + ] + } + }, + "token_rate": { + "kind": "Panel", + "spec": { + "display": { + "name": "Token Rate" + }, + "plugin": { + "kind": "TimeSeriesChart", + "spec": { + "legend": { + "position": "bottom" + } + } + }, + "queries": [ + { + "kind": "TimeSeriesQuery", + "spec": { + "plugin": { + "kind": "PrometheusTimeSeriesQuery", + "spec": { + "datasource": { + "kind": "PrometheusDatasource", + "name": "vm" + }, + "query": "rate(proxy_tokens_input_total[5m]) * 60", + "seriesNameFormat": "input/min" + } + } + } + }, + { + "kind": "TimeSeriesQuery", + "spec": { + "plugin": { + "kind": "PrometheusTimeSeriesQuery", + "spec": { + "datasource": { + "kind": "PrometheusDatasource", + "name": "vm" + }, + "query": "rate(proxy_tokens_output_total[5m]) * 60", + "seriesNameFormat": "output/min" + } + } + } + } + ] + } + }, + "tokens_5h": { + "kind": "Panel", + "spec": { + "display": { + "name": "5h Tokens" + }, + "plugin": { + "kind": "StatChart", + "spec": { + "calculation": "last", + "format": { + "unit": "decimal" + }, + "sparkline": {} + } + }, + "queries": [ + { + "kind": "TimeSeriesQuery", + "spec": { + "plugin": { + "kind": "PrometheusTimeSeriesQuery", + "spec": { + "datasource": { + "kind": "PrometheusDatasource", + "name": "vm" + }, + "query": "increase(proxy_tokens_output_total[3h])" + } + } + } + } + ] + } + }, + "tokens_7d": { + "kind": "Panel", + "spec": { + "display": { + "name": "7d Tokens" + }, + "plugin": { + "kind": "StatChart", + "spec": { + "calculation": "last", + "format": { + "unit": "decimal" + }, + "sparkline": {} + } + }, + "queries": [ + { + "kind": "TimeSeriesQuery", + "spec": { + "plugin": { + "kind": "PrometheusTimeSeriesQuery", + "spec": { + "datasource": { + "kind": "PrometheusDatasource", + "name": "vm" + }, + "query": "increase(proxy_tokens_output_total[9h])" + } + } + } + } + ] + } + }, + "util_5h": { + "kind": "Panel", + "spec": { + "display": { + "name": "5h Utilization" + }, + "plugin": { + "kind": "GaugeChart", + "spec": { + "calculation": "last", + "format": { + "unit": "percent" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 90 + } + ] + } + } + }, + "queries": [ + { + "kind": "TimeSeriesQuery", + "spec": { + "plugin": { + "kind": "PrometheusTimeSeriesQuery", + "spec": { + "datasource": { + "kind": "PrometheusDatasource", + "name": "vm" + }, + "query": "proxy_usage_utilization{window=\"5h\"}" + } + } + } + } + ] + } + }, + "util_7d": { + "kind": "Panel", + "spec": { + "display": { + "name": "7d Utilization" + }, + "plugin": { + "kind": "GaugeChart", + "spec": { + "calculation": "last", + "format": { + "unit": "percent" + }, + "thresholds": { + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 90 + } + ] + } + } + }, + "queries": [ + { + "kind": "TimeSeriesQuery", + "spec": { + "plugin": { + "kind": "PrometheusTimeSeriesQuery", + "spec": { + "datasource": { + "kind": "PrometheusDatasource", + "name": "vm" + }, + "query": "proxy_usage_utilization{window=\"7d\"}" + } + } + } + } + ] + } + } + }, + "layouts": [ + { + "kind": "Grid", + "spec": { + "display": { + "title": "Utilization" + }, + "items": [ + { + "x": 0, + "y": 0, + "width": 6, + "height": 5, + "content": { + "$ref": "#/spec/panels/util_5h" + } + }, + { + "x": 6, + "y": 0, + "width": 6, + "height": 5, + "content": { + "$ref": "#/spec/panels/util_7d" + } + }, + { + "x": 12, + "y": 0, + "width": 6, + "height": 5, + "content": { + "$ref": "#/spec/panels/tokens_5h" + } + }, + { + "x": 18, + "y": 0, + "width": 6, + "height": 5, + "content": { + "$ref": "#/spec/panels/tokens_7d" + } + } + ] + } + }, + { + "kind": "Grid", + "spec": { + "display": { + "title": "Traffic" + }, + "items": [ + { + "x": 0, + "y": 0, + "width": 12, + "height": 8, + "content": { + "$ref": "#/spec/panels/request_rate" + } + }, + { + "x": 12, + "y": 0, + "width": 12, + "height": 8, + "content": { + "$ref": "#/spec/panels/latency" + } + } + ] + } + }, + { + "kind": "Grid", + "spec": { + "display": { + "title": "Tokens" + }, + "items": [ + { + "x": 0, + "y": 0, + "width": 24, + "height": 8, + "content": { + "$ref": "#/spec/panels/token_rate" + } + } + ] + } + } + ], + "duration": "1h", + "refreshInterval": "10s" + } +} diff --git a/package.nix b/package.nix index 9d3df46..2b17ee5 100644 --- a/package.nix +++ b/package.nix @@ -7,11 +7,11 @@ buildGoModule rec { pname = "anthropic-proxy"; - version = "0.0.4"; + version = "0.0.5"; src = ./.; - vendorHash = "sha256-8pq4GYFjOfYcYLcZSuXMWn77RUxVGP18AcyzIJGbKf4="; + vendorHash = "sha256-yXINNC+NEw+HbOQ5aBgSE5dYTWp+zEZ230rzXfwOoDY="; meta = with lib; { description = "Reverse proxy that lets OpenCode (and similar tools) use a Claude subscription instead of an API key.";