Skip to content

Releases: l17728/pairproxy

v2.24.6 — Smart Probe 全面修复 + reportgen UTC 时区归一化

11 Apr 10:19

Choose a tag to compare

v2.24.6

🐛 Bug Fixes

internal/lb — Smart Probe 7 项修复

1. WithTimeout 未同步 prober 客户端

调用 WithTimeout 仅更新了 hc.clienthc.timeout,但 hc.prober 持有独立的 http.Client,导致 health_check_timeout 配置对智能探活(Discover 阶段)的 HTTP 请求完全无效。

修复:WithTimeout 现同时重建 hc.prober = NewProber(d, h.logger),三者超时严格一致。

2. 空白 API Key 被当作有效凭证

injectCredential 原先检查 cred.APIKey == "",纯空格或制表符组成的 key 会通过检查并注入无效的 Authorization header,向上游发出带噪声认证头的请求。

修复:先 strings.TrimSpace() 再判空,空白字符串视为"无凭证",不注入任何认证头。

3. buildProbeURL 未过滤查询参数和 Fragment

当目标地址(addr)包含 ?query#fragment 时,探活 URL 会被拼接成无效路径(如 http://host/v1?key=val/models)。

修复:使用 url.Parse 提取路径,并用 strings.IndexAny(addrBase, "?#") 在构建 addrBase 前截断。

4. Discover 端点超时 vs 连接拒绝未区分

原 Discover 将所有 definitivelyUnhealthy() 错误均视为连接拒绝(unreachable=true),但 HTTP 请求超时也会触发此分支,导致临时超时的目标被错误地标记为不可达。

修复:新增 isEndpointTimeout() helper,区分两类失败:

  • 端点超时(*url.Error.Timeout()context.DeadlineExceeded)→ continue 尝试下一路径
  • 硬连接失败(拒绝连接/DNS 错误)→ 返回 unreachable=true

5. 所有路径超时时错误标记 unreachable

若 Discover 期间所有探活路径均超时(无 HTTP 响应),或上层 context 预算耗尽,原实现语义不清晰,可能与硬连接失败混淆。

修复:通过 gotHTTPResponse 标志和 budgetExhausted 标志明确区分三种结果:

  • 有 HTTP 响应但不匹配 → nil, false(服务在线,路径不符)
  • 所有路径超时 → nil, false(网络不确定,不标记 unreachable)
  • 硬连接失败 → nil, true(unreachable)

6. Start() 重复调用导致 close(stopCh) panic

loop() 退出时关闭 stopCh,若再次调用 Start() 使用同一已关闭的 channel,recovery goroutine 的 select 会在关闭的 channel 上立即返回,错误退出。

修复:Start() 首行重建 stopCh = make(chan struct{})

7. recoveryDelay goroutine 阻塞进程关闭

进程关闭时,等待 recoveryDelay(可达数分钟)的 goroutine 会阻塞 wg.Wait(),导致进程无法及时退出。

修复:time.Sleep(hc.recoveryDelay)select { case <-time.After(hc.recoveryDelay): case <-hc.stopCh: return },关闭信号到达后毫秒级退出。

附加修复

  • UpdateHealthPaths 缓存失效:目标失去显式 health_check_path 时,现立即调用 probeCache.invalidate(),与 UpdateCredentials 行为对齐
  • checkAll() 数据竞争hc.healthPath 现在锁内快照为 globalHealthPath,消除并发读写竞争
  • API 清理:移除 probeEntry.unreachable 字段和 ProbeCache.set()unreachable bool 参数(死代码);ProbeCache.musync.RWMutex 简化为 sync.Mutex

🐛 Bug Fixes

tools/reportgen — SQLite 时区归一化(17 个查询函数)

新增 toUTC() helper,在所有接受 from, to time.Time 参数的查询函数入口统一转 UTC。

根因:SQLite 以字符串存储时间戳,字符串比较是字典序。当调用方传入本地时区时间(如 +08:00),与数据库中 UTC 格式的记录比较会产生错误的过滤结果,导致图表数据为空或范围错误。

修复的函数queries.go):
QueryKPIQueryDailyTrendQueryHeatmapQueryLatencyBoxPlotByModelQueryLatencyPercentileTrendQueryDailyLatencyTrendQueryUserRequestBoxPlot

修复的函数queries_phase8.go):
QueryLatencyHistogramQueryLatencyScatterQueryTokenThroughputHeatmapQueryUpstreamShareQueryUpstreamLatencyTrendQueryCostPerTokenTrendQueryIORatioTrendQueryPeakRPMQueryModelDailyTrendQuerySourceNodeDist


🧪 新增测试(13 个)

测试 覆盖场景
TestInjectCredential_WhitespaceKey 纯空格 key 不产生认证头
TestInjectCredential_TabWhitespaceKey 制表符 key 不产生认证头
TestBuildProbeURL_QueryParams 含查询参数、Fragment 的地址正确构建探活 URL
TestWithTimeout_UpdatesProberClient WithTimeout 同步更新 prober HTTP client 超时
TestWithTimeout_Default 默认超时下 hc.client 与 prober 一致
TestIsEndpointTimeout 6 种错误类型的分类正确性
TestUpdateHealthPaths_InvalidatesCache 失去显式路径的目标缓存被清除
TestUpdateHealthPaths_NoInvalidationForNewTargets 新增目标不触发无效缓存清除
TestDiscover_CtxExpiry_NotUnreachable 预取消 context → nil,false 而非 unreachable
TestIsEndpointTimeout_ContextCanceled context.Canceled 语义文档化
TestProbe_Discover_ContinuesPastEndpointTimeout 端点超时后继续尝试下一路径
TestProbe_Discover_ConnectionRefusedIsUnreachable 连接拒绝 → unreachable=true
TestProbe_Discover_AllMethodsTimeout_NotUnreachable 全部超时 → unreachable=false

📊 测试统计

  • 主模块:2,144 个测试,25 个包,0 FAIL
  • 回归:全量 go test ./... 通过

Full Changelog: v2.24.5...v2.24.6

v2.24.5 — SQLite Timezone Fix + reportgen Parity Tests

09 Apr 14:31

Choose a tag to compare

🔧 Gateway (sproxy/cproxy)

SQLite Timezone Bug Fix

  • Root cause: SQLite stores time.Time as RFC3339 strings. On non-UTC systems, CreatedAt stored as "2026-04-09T20:00:00+08:00" (local) but queries compared against "2026-04-09T12:00:00Z" (UTC). String comparison is lexicographic → wrong ordering → 0 tokens returned.

  • Fix 1 — Storage normalization: Added UsageLog.BeforeCreate GORM hook to normalize all writes to UTC

    func (u *UsageLog) BeforeCreate(tx *gorm.DB) error {
        if !u.CreatedAt.IsZero() {
            u.CreatedAt = u.CreatedAt.UTC()
        }
        return nil
    }
  • Fix 2 — Query normalization: Added toUTC() helper applied to 9 time-filtered methods

    • Query, SumTokens, GlobalSumTokens, UserStats, ExportLogs, SumCostUSD, DailyTokens, DailyCost, DeleteBefore
    • Ensures bounds are UTC before SQLite string comparison
  • Impact: Token statistics on non-UTC systems (Asia/Shanghai, etc.) now return correct values

E2E Test Fixes

  • Updated seedTokens in sproxy_e2e_test.go and f10_features_e2e_test.go to use time.Now().UTC()

📊 reportgen Tool

26 New Unit Tests (queries_extra_test.go)

Previously 36 of 39 query functions had zero test coverage. Now all critical functions tested:

  • QueryTopUsers, QueryGroupComparison, QueryStreamingRatio, QueryDailyTrend
  • QueryEngagement, QueryQuotaUsage, QueryGroupTokenDistribution, QueryModelTokenBoxPlots
  • QuerySourceNodeDist, QueryUpstreamStats, QueryStatusCodeDist, QueryPeakRPM
  • QuerySlowRequests, QueryErrorRequests

SQLite vs PostgreSQL Parity Tests (pg_parity_test.go)

Major addition: Every Query* function now has a deterministic parity test ensuring identical output across backends.

23 tests:

  • TestPGParity_<QueryName> for 22 functions — seeds identical data into both SQLite and PostgreSQL, runs query, asserts field-by-field equality
  • TestPGParity_AllQueries — smoke test running all 23 Query* functions in parallel

Example assertion:

for i, pg := range pgRows {
    sq := sqRows[i]
    if pg.TotalTokens != sq.TotalTokens {
        t.Errorf("[%d] TotalTokens: PG=%d SQLite=%d", i, pg.TotalTokens, sq.TotalTokens)
    }
}

How to run:

POSTGRES_TEST_DSN="postgres://user:pass@localhost:5432/reportgen_test?sslmode=disable" \
  go test -v -run TestPGParity ./tools/reportgen/

Deterministic Query Ordering

Fixed 2 bugs where ties in primary sort key caused different output order:

  • QueryStatusCodeDist: Added status_code ASC to tie-break when cnt is identical

    ORDER BY cnt DESC, status_code ASC
  • QueryQuotaUsage: Added u.username ASC to tie-break when monthly_used is identical

    ORDER BY monthly_used DESC, u.username ASC

Error Visibility

  • generator.go: Changed silent _, _ = query discards to warnQueryErr() — logs WARNING: <QueryName> failed: <err> to stderr
  • queries.go loadMaps: Explicit WARNING: on Scan errors instead of continuing silently
  • integration_test.go: Fail-fast on sql.Open/NewQuerier errors

📚 Documentation Refresh

8 files updated for v2.24.5:

  1. CHANGELOG.md — Full v2.24.5 entry with timezone root cause, GORM hook, toUTC() helper explanation
  2. UPGRADE.md — v2.24.5 section with schema/code changes/verification steps + rollback guidance
  3. TEST_REPORT.md — Date/version updated; test count: 2,078 (main) + 43 (reportgen) = 2,121
  4. README.md — Added 3 new feature rows for v2.24.2/v2.24.3/v2.24.5
  5. CONFIGURATION.md — New "reportgen 工具参数" section (basic, PostgreSQL, LLM flags, examples)
  6. DEPLOYMENT.md — New "reportgen 部署" section (build, usage, crontab, fault-tolerance)
  7. TESTING.md — Added reportgen file structure, test count inventory
  8. test/e2e/README.md — Added group_target_set_e2e_test.go to file list; 17 test files total

✅ Test Coverage

Main modules: 2,078 tests (25 packages) — ALL PASS

  • github.com/l17728/pairproxy/internal/db
  • github.com/l17728/pairproxy/internal/proxy
  • github.com/l17728/pairproxy/test/e2e
  • ... and 22 others

reportgen (SQLite only): 59 tests — ALL PASS

  • queries_extra_test.go: 26 tests
  • integration_test.go: 17 tests
  • LLM, utility tests: 16 tests

reportgen (SQLite + PostgreSQL): 78 tests — ALL PASS

  • All 59 SQLite tests
  • 19 new PostgreSQL parity tests

Total: 2,156 tests across both gateway and reportgen. 0 FAIL, 0 SKIP (with POSTGRES_TEST_DSN set).


🔗 Commits

  • 4da6c42 feat(v2.24.5): SQLite timezone normalization + reportgen deterministic ordering + dual-driver parity

🚀 Installation

Binary Release

Available on Releases

From Source

git clone https://github.com/l17728/pairproxy.git
cd pairproxy
git checkout v2.24.5
make build

Upgrade

  1. Backup database: cp pairproxy.db pairproxy.db.bak
  2. Stop sproxy: systemctl stop sproxy
  3. Replace binary: cp sproxy-v2.24.5 /usr/local/bin/sproxy
  4. Start: systemctl start sproxy (AutoMigrate runs automatically)
  5. Verify: curl http://localhost:9000/health

See UPGRADE.md for detailed steps.

Full Changelog: v2.24.4...v2.24.5

v2.24.4 — Reportgen: Bug Fixes, Chart Tooltips & Enhanced Tests

09 Apr 10:08

Choose a tag to compare

本版本说明

本版本仅包含 reportgen 报告生成工具的更新,不涉及网关(pairproxy)核心代码,无需重新部署网关二进制


下载说明

每个压缩包包含:

  • reportgen 可执行文件(Windows 为 reportgen.exe
  • templates/report.html HTML 模板(必须与可执行文件放在同一目录)
平台 文件
Linux x86_64 reportgen-v2.24.4-linux-amd64.tar.gz
Linux ARM64 reportgen-v2.24.4-linux-arm64.tar.gz
macOS x86_64 reportgen-v2.24.4-darwin-amd64.tar.gz
macOS Apple Silicon reportgen-v2.24.4-darwin-arm64.tar.gz
Windows x86_64 reportgen-v2.24.4-windows-amd64.zip

快速开始

# 解压后,在同一目录执行:
./reportgen -db /path/to/pairproxy.db          # 默认分析过去 30 天
./reportgen -db /path/to/pairproxy.db -from 2026-04-01 -to 2026-04-09 -output report.html

# PostgreSQL
./reportgen -pg-dsn "postgres://user:pass@host:5432/dbname"

# 启用 AI 洞察
./reportgen -db pairproxy.db -llm-url https://api.anthropic.com -llm-key sk-xxx -llm-model claude-haiku-4-5

注意templates/report.html 必须存在于运行目录的 templates/ 子目录下,或通过 -template 参数指定路径。


v2.24.4 变更内容

🐛 Bug 修复

  • Anthropic 路由修复:当 llm_targets 表中同时设置了 provider=anthropicmodel 字段时,请求错误地走 OpenAI /v1/chat/completions 协议,现已修复为始终按 provider 字段判断
  • 无效模型 ID 修复:默认 Anthropic 模型从 claude-haiku-4-5-20251001(不存在)修正为 claude-haiku-4-5
  • 空库 NULL 崩溃修复SUM(CASE WHEN...) 在空表时返回 NULL 导致 Scan 崩溃,已对所有相关列加 COALESCE(..., 0) 保护
  • HTTP 413 重试修复:LLM 网关返回 413(请求体过大)时,现在正确触发裁剪数据后重试
  • 死图表修复:移除 initIORatio() 无效调用(无对应 div),新增 chart-upstream-share(上游请求占比饼图)并接入 3 列 Grid 布局
  • 拼写错误修复sticknessstickiness,保留兼容 fallback
  • 日期参数优化-from / -to 现为可选,默认分析过去 30 天至今

✨ 新增功能

  • 图表悬停说明:所有 35 个图表标题旁新增 ❓ 图标,鼠标悬停即显示该图表的用途、关注点和使用方法,无需查阅文档
  • Mock 数据生成器:新增 cmd/mockdata 工具,生成含边界数据(零 Token 请求、NULL model、错误请求)的测试数据库

🧪 测试

新增 integration_test.go,共 32 个测试用例:

  • LLM 路由(OpenAI / Anthropic / 默认模型 / 无 Key / 4xx / 413 重试)
  • SQL 查询(空库、有数据、NULL model、零 Token、llm_targets JOIN)
  • 工具函数(isContextTooLong 11 个子 case、endOfDay 时区、safeKeyPrefixtruncate
  • 驱动初始化(默认 sqlite、无效 DSN)

升级说明

组件 是否需要升级
网关二进制(pairproxy) ❌ 无需升级
reportgen 工具 ✅ 建议升级
数据库 Schema ❌ 无变更
HTML 模板(templates/report.html) ✅ 随二进制包一起更新

Full Changelog: v2.24.3...v2.24.4

v2.24.3 — Issue #6 关闭:同一 URL 多 API Key 正式可用

08 Apr 08:19

Choose a tag to compare

修复摘要

🐛 关键 Bug 修复:Issue #6 正式关闭

问题:同一 URL 配置多个不同 API Key 时,第二次创建总是失败。

根本原因generateID() 使用 time.Now().UnixNano() base36 编码生成 LLM target ID。当两次创建请求在同一纳秒内执行(常见于测试和高并发场景),产生相同 ID,触发 UNIQUE constraint failed: llm_targets.id,与复合约束 (url, api_key_id) 完全无关。

修复generateID() 改用 uuid.NewString(),从根本上消除 ID 碰撞。

复合约束一致性(举一反三全���修复)

通过系统性 3 轮递归分析发现并修复的相关问题:

问题 修复
GetByGroupID() 一对多建模为一对一 ListByGroupID(),返回全部集合
GetDefault() 不支持多个全局默认集 ListDefaults(),返回所有默认集
FindForUser() 使用 First() 有歧义风险 → 防御性 Find() + slice 长度检查
FindByProviderAndValue() 多结果静默返回第一条 → 多结果时返回明确错误

🧪 测试

  • 2077 个测试用例,全部通过
  • 新增并发竞态测试(10 goroutine 并发创建、2 goroutine 并发绑定)
  • 新增 NULL 值处理回归测试
  • 新增 Issue #6 专项回归测试

reportgen 工具增强

本 Release 包含最新 reportgen Windows/amd64 预编译版本(支持 SQLite + PostgreSQL)。

新功能

  • 直接 LLM 配置-llm-url-llm-key 参数,可直接指定 LLM 端点和 API Key,无需数据库配置
  • 命令行优先:若指定了 -llm-url-llm-key,优先使用;否则从数据库查询

可靠性改进

  • 完善的容错机制

    • 数据库查询失败 → 跳过,继续处理
    • LLM 连接失败 → 降级为规则分析,仍可生成有意义的报告
    • LLM 调用异常(panic)→ 捕获恢复,不影响主流程
    • 模板文件缺失 → 使用内置最小模板渲染
    • 无数据时段 → 智能检测并生成提示洞察
  • 改进的错误日志:区分 HTTP 错误(如 429)与网络连接失败,提供更有针对性的诊断

版本兼容性

  • 完整支持 v2.15.0+ 数据库架构,自动适配不同版本

跨平台版本由 CI 自动构建后附于 Release Assets。

升级

无 Schema 变更,无需数据迁移,直接替换二进制即可。

详见 UPGRADE.md

Full Changelog: v2.24.2...v2.24.3

Full Changelog: v2.24.2...v2.24.3

Full Changelog: v2.24.2...v2.24.3

v2.24.2 - PostgreSQL Support

07 Apr 04:22

Choose a tag to compare

Features

  • PostgreSQL Support: reportgen now supports both SQLite and PostgreSQL databases
  • Backward Compatible: Existing SQLite deployments continue to work without changes
  • Flexible Connection Options: Support for PostgreSQL DSN string or individual connection parameters
  • SSL/TLS Support: Full SSL/TLS configuration options for secure database connections

Technical Details

Database Abstraction Layer

  • Automatic SQL dialect conversion (SQLite ↔ PostgreSQL)
  • Parameter binding transformation (? → $1, $2, ...)
  • Helper functions for date/time operations across dialects

Connection Methods

SQLite (existing):

reportgen -db /path/to/database.db [other flags]

PostgreSQL (DSN):

reportgen -pg-dsn "postgres://user:password@localhost:5432/dbname" [other flags]

PostgreSQL (individual parameters):

reportgen -pg-host localhost -pg-port 5432 -pg-user user -pg-password pass -pg-dbname dbname [other flags]

Deployment

  • Stateless Architecture: No local YAML configuration files required
  • Network-Agnostic: Can run on any node with database network access
  • Multi-Node Support: Horizontal scaling without master/slave constraints
  • CI/CD Ready: Works with Kubernetes, Docker Compose, Cron jobs

See DEPLOYMENT_GUIDE.md for comprehensive deployment patterns and best practices.

Documentation

Compatibility

Database Support Status
SQLite 3 Fully supported
PostgreSQL 12+ Fully supported

Breaking Changes

None. This is a backward-compatible release.

v2.24.1

05 Apr 16:36

Choose a tag to compare

Full Changelog: v2.24.0...v2.24.1

v2.24.0

04 Apr 12:51

Choose a tag to compare

v2.24.0 — Model-Aware Routing(模型感知路由)

本版本引入 Model-Aware Routing 功能,让网关能够根据请求中的模型名称智能路由到支持该模型的目标,同时保持完全向后兼容。


新功能

F1 — Config-as-Seed(配置即种子)

配置文件中的 LLM target 现在作为初始种子写入数据库:

  • 首次启动时,sproxy.yaml 中的 target 自动同步到数据库
  • 若 target URL 已存在(WebUI 已添加或修改),跳过覆盖,保留 WebUI 的修改
  • 配置文件负责"提供初始值",运行时由 WebUI 管理

F2 — Per-Target Supported Models(按目标过滤模型)

每个 LLM target 可声明自己支持哪些模型,网关路由时自动过滤:

# sproxy.yaml
llm:
  targets:
    - url: "https://api.anthropic.com/v1"
      provider: "anthropic"
      weight: 10
      supported_models:
        - "claude-3-opus-20250119"
        - "claude-3-sonnet-20250219"
        - "claude-3-haiku-*"    # 前缀通配
        - "claude-2.1"          # 精确匹配
      auto_model: "claude-3-sonnet-20250219"

    - url: "https://api.openai.com/v1"
      provider: "openai"
      weight: 8
      supported_models:
        - "gpt-4-*"
        - "gpt-3.5-turbo"
      auto_model: "gpt-4-turbo"

    - url: "http://localhost:11434"
      provider: "ollama"
      supported_models: []    # 空 = 接受所有模型
      auto_model: "mistral"

模式匹配规则:精确匹配、前缀通配(claude-3-*)、全通配(*)、空列表(接受所有)。

Fail-Open 策略:请求的模型不在任何 target 的配置中时,网关不拒绝请求,回退到所有健康 target,由 LLM 自行决定是否接受。

F3 — Auto Mode(自动模型选择)

客户端发送 "model": "auto" 时,网关自动为每个目标选择合适的模型并改写请求体:

客户端: {"model": "auto", ...}
  → Anthropic target: {"model": "claude-3-sonnet-20250219", ...}
  → OpenAI target:    {"model": "gpt-4-turbo", ...}

降级策略:auto_model 字段 → supported_models[0] → 透传原始值。


API 与 CLI 支持

REST APIPOST / PUT /api/admin/llm/targets)新增字段:

  • supported_models: 字符串数组,模型过滤列表
  • auto_model: 字符串,auto 模式默认模型

CLI 命令sproxy admin llm target add / update)新增 flags:

  • --supported-models "claude-3-*,claude-2.1" — 逗号分隔,支持通配符
  • --auto-model "claude-3-sonnet-20250219" — auto 模式目标模型

测试

  • 新增 17 个单元测试,覆盖:模式匹配、请求体改写、模型过滤、auto 模型选择
  • 全部 1991 个测试回归通过,0 失败,0 数据竞争

文档

  • docs/manual.md §43 — 完整运维指南(配置、API、CLI、故障排查、升级步骤)
  • .sisyphus/CONFIGURATION_GUIDE.md(552 行)— 场景说明 + 完整 sproxy.yaml 示例 + 故障排查映射
  • .sisyphus/LOGGING_SPECIFICATION.md — 所有路由决策的结构化日志规范

升级指南

无数据库手动操作,Schema 自动迁移(新增 supported_modelsauto_model 两列)。

  1. 替换二进制:./sproxy version 应输出 sproxy v2.24.0
  2. (可选)在 sproxy.yaml 各 target 中添加 supported_modelsauto_model
  3. 重启 sproxy,查看日志确认迁移完成

完全向后兼容:未配置 supported_models 的 target 行为不变;未配置 auto_model"auto" 模式透传。


Full Changelog: v2.23.0...v2.24.0

v2.23.0

03 Apr 23:55

Choose a tag to compare

What's Changed

  • Review: Issues #2, #3, #4 - Comprehensive fixes and improvements by @l17728 in #5

Full Changelog: v2.22.1...v2.23.0

v2.22.1: Complete Documentation Update

28 Mar 09:22

Choose a tag to compare

v2.22.1 — Documentation & Project Docs Comprehensive Update

Documentation Updates

This release includes comprehensive documentation refresh across all project documents to v2.22.0:

User Manual

  • docs/manual.md: Updated to v2.22.0
    • §39 WebUI Phase 1: Group-Target Set Management (dual-panel UI, 6 API endpoints, member operations)
    • §40 WebUI Phase 2: Alert Management Enhancement (Live/Active/History tabs, batch operations, SSE)
    • §41 WebUI Phase 3: Quick Operations Panel (3 summary cards, async loading, graceful degradation)
    • +334 lines of detailed operational guidance with configuration examples

Project Reports & Guides

  • ACCEPTANCE_REPORT.md: Updated to v2.22.0 (WebUI Expansion Phase 1/2/3)
  • TEST_REPORT.md: Updated test version; 1,956 total tests, 34 E2E WebUI tests, all passing
  • UPGRADE.md: Current version updated from v2.20.0 to v2.22.0
  • OPENCLAW_OPS_GUIDE.md: Updated to v2.5, compatible with v2.22.0+

No Code Changes

This is a documentation-only patch. All features are already available in v2.22.0. No binaries needed.

What's Included

  • ✅ Complete WebUI Phase 1/2/3 operational documentation
  • ✅ API endpoint specifications for all new features
  • ✅ Configuration examples and use case scenarios
  • ✅ Updated test statistics and acceptance criteria
  • ✅ Comprehensive upgrade guidance

Deployment

If already running v2.22.0, no action needed. Simply update your documentation reference to include these new chapters for complete feature guidance.


Full Changelog: v2.22.0...v2.22.1

Full Changelog: v2.22.0...v2.22.1

v2.22.0: WebUI Expansion (Phase 1/2/3)

28 Mar 09:02

Choose a tag to compare

PairProxy v2.22.0 Release Notes

Release Date: March 28, 2026
Git Tag: v2.22.0
Base Version: v2.21.1


📝 Overview

v2.22.0 introduces the WebUI Expansion Phase 1/2/3, bringing comprehensive new management capabilities to the PairProxy dashboard. This release adds group-based target set management, enhanced alert dashboard, and quick operations panel for improved operational visibility.

Key Metrics:

  • ✅ 34 E2E integration tests passing
  • ✅ 197 unit test files
  • ✅ 3 major feature phases completed
  • ✅ Zero breaking changes

🚀 Phase 1: Group-Target Set Management

What's New

A new GroupTargetSet resource enables administrators to:

  • Group multiple LLM targets into logical sets
  • Bind target sets to user groups for flexible routing
  • Manage target set members independently
  • Track usage and health per target set

User Interface

Dual-panel layout in the LLM dashboard:

  • Left panel: Target sets list with quick actions (edit, delete)
  • Right panel: Member list, group binding, and statistics
  • Tab navigation: Easy switching between Targets, Target Sets, and Bindings

API Endpoints

Method Endpoint Purpose
POST /dashboard/llm/targetsets Create new target set
POST /dashboard/llm/targetsets/{id}/update Update target set details
POST /dashboard/llm/targetsets/{id}/delete Delete target set
POST /dashboard/llm/targetsets/{id}/members Add member to target set
POST /dashboard/llm/targetsets/{id}/members/update Update member configuration
POST /dashboard/llm/targetsets/{id}/members/delete Remove member from target set

Features

  • Validation: Target set IDs must match [a-zA-Z0-9_-]+ pattern
  • Audit logging: All operations logged for compliance
  • Worker node protection: Read-only mode for worker nodes
  • Flash messages: User feedback with proper error encoding

Database Schema

CREATE TABLE group_target_sets (
    id TEXT PRIMARY KEY,
    group_id TEXT NOT NULL,
    name TEXT NOT NULL,
    strategy TEXT,
    created_at TIMESTAMP,
    FOREIGN KEY (group_id) REFERENCES groups(id)
);

CREATE TABLE group_target_set_members (
    id TEXT PRIMARY KEY,
    target_set_id TEXT NOT NULL,
    target_url TEXT NOT NULL,
    created_at TIMESTAMP,
    FOREIGN KEY (target_set_id) REFERENCES group_target_sets(id)
);

🎯 Phase 2: Alert Management Enhancement

What's New

Complete alert dashboard redesign with three-tab interface:

  1. Live Tab

    • Real-time event streaming
    • Level filtering (All, Error, Warning)
    • Updates without page reload
  2. Active Tab

    • Alert statistics with severity cards
    • Checkbox selection for batch operations
    • Quick resolution with visual feedback
    • Shows: Critical count, Error count, Warning count
  3. History Tab

    • 90-day historical query
    • Time range selector (7, 30, 90 days)
    • Level and source filtering
    • Pagination (50 items per page)

API Endpoints

Method Endpoint Purpose
POST /dashboard/alerts/resolve Resolve single alert
POST /dashboard/alerts/resolve-batch Batch resolve multiple alerts

Features

  • Non-blocking: Async data loading
  • Pagination: 50 items per page with Previous/Next
  • Filtering: By level, source, and time range
  • Batch operations: Select multiple, resolve together
  • Status badges: Color-coded severity indicators

⚡ Phase 3: Quick Operations Panel

What's New

Dashboard summary cards providing at-a-glance operational status.

Summary Cards

  1. LLM 目标状态 (LLM Target Status)

    • Healthy targets count
    • Targets with alerts count
    • Total target sets count
  2. 系统告警 (System Alerts)

    • Unresolved alerts count
    • Error count
    • Warning count
    • Status badge (green/yellow/red)
  3. 用户/分组 (Users/Groups)

    • Active users count
    • Total groups count
    • New users count

Implementation

  • Async loading: Data fetched via AJAX after page load
  • Graceful degradation: Missing data shows as "—" or "未配置"
  • Real-time updates: Refreshes on page initialization
  • Zero latency: Non-blocking dashboard render

Data Sources

  • Alert counts: /api/dashboard/events
  • User/group stats: /dashboard/api/user-stats

🐛 Critical Bug Fixes

1. GORM Zero-Value Bug

Issue: IsActive=false not persisting in AddMember operation
Cause: GORM treats false as empty value in insert
Fix: Explicit Update call after insert, comprehensive test coverage
Impact: Member activation state now reliable

2. Template Scope Issue

Issue: Panic when accessing SelectedSetID inside range context
Cause: Context shadowing in {{range .TargetSets}}
Fix: Changed to $.SelectedSetID to access parent scope
Impact: Target set selection now works correctly

3. Member Route Handling

Issue: Delete/update member operations return 404 for complex URLs
Cause: Go 1.22 router doesn't allow {param} spanning multiple / segments
Fix: Changed route from /members/{memberID}/delete to /members/delete with POST form fields
Impact: All member operations now functional

4. URL Encoding in Redirects

Issue: Malformed HTTP Location headers with special characters
Cause: Unencoded error messages containing &, =, ?, spaces
Fix: Wrapped all with neturl.QueryEscape()
Impact: Error messages display correctly

5. Modal Field Population

Issue: Edit modal opens blank, admin edits with empty fields
Cause: editTargetSet() didn't populate form before opening
Fix: Read data-* attributes and populate modal fields
Impact: Edit operations now show current values

6. Package Shadowing

Issue: Compiler error: url.QueryEscape undefined
Cause: Local url variable shadowed net/url package
Fix: Import alias neturl and rename variable to targetURL
Impact: Clean compilation


📊 Testing Coverage

E2E Tests

  • TestGroupTargetSetAPI
  • TestAlertAPI
  • TestCreateTargetSet
  • TestAlertStats
  • All 34 integration tests passing

Unit Tests

  • Database operations: ✅
  • Authentication/JWT: ✅
  • Usage logging: ✅
  • Quota enforcement: ✅

Pre-Existing Issues

  • LLM UI binding tests (13 failures) - Existing before v2.22.0
  • Not introduced by Phase 1/2/3 changes

🔄 Migration Guide

For Users on v2.21.x

Step 1: Update Code

git pull origin main
git checkout v2.22.0

Step 2: Build

go build ./cmd/cproxy

Step 3: Run

./cproxy  # Database migrations automatic

No Manual Steps Required:

  • ✅ Schema updates automatic
  • ✅ Existing data preserved
  • ✅ No configuration changes needed
  • ✅ Features available immediately

Breaking Changes

None. All changes are:

  • ✅ Additive only
  • ✅ Backward compatible
  • ✅ Non-breaking to existing APIs

🔐 Security Enhancements

Session Protection

  • All new endpoints require valid session
  • CSRF protection via form tokens
  • Session middleware chain validation

Access Control

  • Worker nodes: Read-only mode for all operations
  • Write protection: Only Admin nodes can modify
  • Group-based routing: Enforced at handler level

Input Validation

  • Target set ID format validation: [a-zA-Z0-9_-]+
  • URL encoding: All user input sanitized
  • SQL injection prevention: GORM parameterized queries

Audit Logging

  • All write operations logged
  • Timestamp and user tracking
  • Compliance-ready audit trail

📈 Performance

Dashboard Load Time

  • Before: Full page render blocking
  • After: Async card loading, sub-100ms render time

Database Queries

  • Indexed queries for common operations
  • Cached mappings during request
  • Pagination limits result sets

Browser Experience

  • Non-blocking initialization
  • Graceful fallbacks
  • Mobile-optimized layout

🛠️ Technical Details

Technology Stack

Component Version Purpose
Go 1.22+ Backend runtime
SQLite Latest Data persistence
Tailwind CSS v4 (CDN) Styling
html/template Built-in Server-side rendering

Architecture

Server-Side Rendering (SSR)

  • No frontend framework required
  • Tailwind CSS via CDN
  • Go 1.22 net/http patterns
  • Modal dialogs with CSS toggling

Database

  • SQLite with WAL mode
  • Automatic schema migrations
  • Table-driven versioning
  • Foreign key constraints

Middleware Chain

  • Session validation
  • Write-node protection
  • Request logging
  • Error handling

📚 Documentation

New/Updated Documents

  • CHANGELOG.md - Comprehensive change log
  • docs/ARCHITECTURE.md - Updated with v2.22 changes
  • docs/DATABASE.md - Schema documentation
  • API endpoint documentation

Available Resources

  • Code comments for implementation details
  • Test cases as usage examples
  • Git history for debugging

🔗 Related Issues

Fixed by this release:

  • Group-Target Set feature completion
  • Alert dashboard enhancement
  • Dashboard quick ops panel
  • Package import shadowing

📋 Dependency Changes

No new dependencies added.

All features implemented with existing dependencies:

  • ✅ No external packages
  • ✅ No version bumps
  • ✅ Same build time

🎉 What Users Get

For Administrators

  • ✅ Flexible target set grouping
  • ✅ Enhanced alert management
  • ✅ Dashboard overview
  • ✅ Audit trail

For Operations

  • ✅ Real-time alert visibility
  • ✅ Batch operations
  • ✅ Historical query
  • ✅ Health status at a glance

For Developers

  • ✅ New API endpoints
  • ✅ Database schema
  • ✅ UI patterns
  • ✅ Complete documentation

🚀 Known Limitations

  1. Quick Ops data: Requires alert service running
  2. History retention: Limited by database (typically 90 days UI limit)
  3. Real-time updates: Polling-based, not WebSocket
  4. *...
Read more