Repository navigation
llm v0.15.0
下游要做什么
什么都不用做——纯增量:字段缺省为 nil 时行为逐字不变,现有调用方不需要改。
想给 LLM 出站挂守卫的宿主,改一行:
hc, err := netguard.NewClient(nil, netguard.Allow{Private: true, Loopback: true})
cfg.HTTPClient = hc // hc.Transport 挂了 netguard.DialFunc(netguard 在 agent-harness 仓;这里是说明接线形态,本仓不依赖它。)
为什么需要
写面校验(按 URL 判)拦不住 DNS rebinding——两次解析之间权威 DNS 可以改口:
写面校验:evil.example → 1.2.3.4 (公网,放行)
实际请求:evil.example → 169.254.169.254 (元数据段,已不在判定面内)
要闭合它,守卫必须挂在 Transport.DialContext 上,这要求宿主能提供自己的 *http.Transport(或整个 *http.Client)。而 LLM 档案的 base_url 是用户可配的(平台代发 LLM 请求的目标),是这条链上暴露最大的一个面。
语义:显式注入优先于内部构造
| 宿主注入 | 捕获 Retry-After | 行为 |
|---|---|---|
| 否 | 否 | 返回 nil——走 openai 包内部缺省(既有行为逐字不变) |
| 否 | 是 | 自建 client(既有行为逐字不变) |
| 是 | 否 | 原样使用宿主注入的 client |
| 是 | 是 | 在宿主注入的 Transport 外面套一层 RetryAfterTransport |
最后一行是此前做不到的组合:自建 client 走 http.DefaultTransport,宿主的守卫挂不上去——CaptureRetryAfter 与自定义守卫此前是互相排斥的。现在合流。
派生时复制一份 client,不就地改写宿主传进来的那个。
超时语义:注入后由注入的 Client 完全接管(eino 侧同一口径——设了 HTTPClient 就不再用 config.Timeout)。
顺带抬的地板
oekit/pkg v0.18.0 → v0.20.0
oekit/concurrency v0.18.0 → v0.19.0
对本仓零行为差异:本仓只 import concurrency/async 与 concurrency/breaker,这两个包在 v0.18.0→v0.19.0 之间一字未动。
门禁
go vet / test -race ✅ 5 包全绿
gofmt -l . ✅ 空
golangci-lint ✅ 0 issues