Skip to content

Commit f9037a8

Browse files
Patel230opencode
andauthored
fix: audit remediation — runtime split, CI repair, lint clean (#124)
* fix: normalize to 0.0.1 and close gaps - README 22->28 providers + 6 missing rows, Quick Start to engine facade - .env.example fill 28 provider keys - docs/ARCHITECTURE fix tree + endpoints /v1/chat/completions /rerank /ready + 8->28 - docs/README index add HOST-ENGINE-BOUNDARY + enterprise - engine/types.go add 6 aliases ChatOptions/ContinuationConfig/StreamResult/ResponseFormat/ImageURLPart/InputAudioPart * refactor: split provider runtime and add instance-owned routing * fix(ci): repair renamed guard script and fuzz path; add additive contracts and remediation plan CI was non-functional on this branch: three jobs invoked the deleted scripts/check-client-layering.sh and the fuzz job targeted the removed ./client package. Also format five files and add additive DTO fields (ProviderBlock, CallWarning, StreamErrorInfo, RawArguments, ProviderMetadata, route deployment id) that later work packages depend on. Adds docs/plans/audit-remediation.md. * fix: resolve golangci-lint failures blocking CI * fix: replace hard tabs in README Go snippet for markdownlint --------- Co-authored-by: opencode <opencode@graycode.ai>
1 parent 0b81b99 commit f9037a8

301 files changed

Lines changed: 4178 additions & 2301 deletions

File tree

Some content is hidden

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

‎.env.example‎

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,32 @@ FLUX_API_KEY=
66
OPENAI_API_KEY=
77
ANTHROPIC_API_KEY=
88
GEMINI_API_KEY=
9+
DEEPSEEK_API_KEY=
10+
GROQ_API_KEY=
11+
KIMI_API_KEY=
12+
MOONSHOT_API_KEY=
13+
ZAI_API_KEY=
14+
ZAI_CODING_API_KEY=
15+
XIAOMI_MIMO_PAYG_API_KEY=
16+
XIAOMI_MIMO_TOKEN_PLAN_API_KEY=
17+
MINIMAX_API_KEY=
18+
MINIMAX_TOKEN_PLAN_API_KEY=
19+
AZURE_OPENAI_API_KEY=
20+
AWS_SECRET_ACCESS_KEY=
21+
VERTEX_ACCESS_TOKEN=
922
OPENROUTER_API_KEY=
23+
CONCENTRATE_API_KEY=
24+
OPENGATEWAY_API_KEY=
25+
STEPFUN_API_KEY=
26+
AGNES_API_KEY=
27+
LONGCAT_API_KEY=
28+
FIREWORKS_API_KEY=
1029
CANOPYWAVE_API_KEY=
30+
POOLSIDE_API_KEY=
31+
CLINE_API_KEY=
32+
OPENCODEGO_API_KEY=
1133
XAI_API_KEY=
12-
ZAI_API_KEY=
34+
OLLAMA_BASE_URL=
1335

1436
# Default model overrides
1537
OPENAI_MODEL=gpt-4o
@@ -24,8 +46,5 @@ GEMINI_MODEL=gemini-2.0-flash
2446
# FLUX_MODEL_CATALOG_PATH=
2547
# FLUX_MODEL_CATALOG_REFRESH=
2648

27-
# OpenAI-compatible dynamic provider opt-in (1/true/yes)
28-
# FLUX_ALLOW_DYNAMIC_PROVIDERS=
29-
3049
# Local config/state directory
3150
FLUX_CONFIG_DIR=~/.flux

‎.github/workflows/ci.yml‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
- name: Boundary guard
4848
run: |
4949
bash ./scripts/check-ecosystem-boundaries.sh
50-
bash ./scripts/check-client-layering.sh
50+
bash ./scripts/check-provider-layering.sh
5151
- name: gofumpt
5252
run: |
5353
go install mvdan.cc/gofumpt@v0.10.0
@@ -128,7 +128,7 @@ jobs:
128128
- name: Boundary guard
129129
run: |
130130
bash ./scripts/check-ecosystem-boundaries.sh
131-
bash ./scripts/check-client-layering.sh
131+
bash ./scripts/check-provider-layering.sh
132132
- name: Run golangci-lint
133133
run: |
134134
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.0
@@ -150,7 +150,7 @@ jobs:
150150
- name: Boundary guard
151151
run: |
152152
bash ./scripts/check-ecosystem-boundaries.sh
153-
bash ./scripts/check-client-layering.sh
153+
bash ./scripts/check-provider-layering.sh
154154
- name: Test with race detector
155155
run: go test ./... -race -count=1 -shuffle=on -coverprofile=coverage.out -covermode=atomic -timeout=300s
156156
- name: Coverage summary
@@ -266,10 +266,10 @@ jobs:
266266
cache: true
267267
- name: Run fuzz targets
268268
run: |
269-
go test -fuzz=FuzzSanitizeMessages -fuzztime=60s ./client
270-
go test -fuzz=FuzzMergeConsecutiveRoles -fuzztime=60s ./client
271-
go test -fuzz=FuzzBuildCacheKey -fuzztime=60s ./client
272-
go test -fuzz=FuzzGuardrailsCheck -fuzztime=60s ./client
269+
go test -fuzz=FuzzSanitizeMessages -fuzztime=60s ./provider
270+
go test -fuzz=FuzzMergeConsecutiveRoles -fuzztime=60s ./provider
271+
go test -fuzz=FuzzBuildCacheKey -fuzztime=60s ./provider
272+
go test -fuzz=FuzzGuardrailsCheck -fuzztime=60s ./provider
273273
274274
# -------------------------------------------------------------------------
275275
# 10. Cross-platform build matrix — zero CGO, all targets.

‎AGENTS.md‎

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ make ci # Full CI suite
3131
## Architecture
3232

3333
- `engine/` — stable host-facing provider engine facade and DTO contract
34-
- `client/core/` — provider-neutral wire types, transport, stream, and retry primitives
35-
- `client/adapters/` — provider protocol adapters and construction registry
36-
- `client/` — backwards-compatible public facade, middleware, and caches
34+
- `provider/core/` — provider-neutral wire types, transport, stream, and retry primitives
35+
- `provider/adapters/` — provider protocol adapters and construction registry
36+
- `provider/` — provider runtime composition root
3737
- `credentials/` — API key storage, lookup, and safe status projection
3838
- `catalog/` — model catalog, discovery, capabilities, and pricing
3939
- `router/` and `runtime/` — route policy and runtime resolution
@@ -50,13 +50,13 @@ make ci # Full CI suite
5050
## Common Pitfalls
5151

5252
- `engine`, `llm`, `graph` and `tools` are the host contract surface. Rho
53-
must not assemble `client`, `catalog`, `config`, `credentials`, `router` or
53+
must not assemble `provider`, `catalog`, `config`, `credentials`, `router` or
5454
`runtime`. Six symbols Rho needs (`ChatOptions`, `ContinuationConfig`,
5555
`StreamResult`, `ResponseFormat`, `ImageURLPart`, `InputAudioPart`) live in
5656
`llm` with no `engine` alias; widening the facade to cover them is a
5757
deliberate API change, not an incidental one.
58-
- `client.Provider` remains the lower-level compatibility boundary for other
59-
consumers; preserve its method set and the facade's type identity
58+
- `provider/core.Provider` is the lower-level provider contract; keep its
59+
method set stable and use it across feature packages
6060
- Streaming tests need careful goroutine management
6161
- `go.work` here should stay minimal; the parent `graycode-eco/go.work`
6262
connects this independent `flux` checkout beside Rho for local development.
@@ -65,20 +65,20 @@ make ci # Full CI suite
6565

6666
## Naming Conventions
6767

68-
- **Provider interface**: `client.Provider` with `Chat()`, `StreamChat()`, `Ping()`, `Name()` — implemented per LLM vendor
69-
- **Client types**: `FluxClient`, `FluxMessage`, `FluxResponse`, `FluxTool`, `FluxUsage` — `Flux` prefix for public types
70-
- **Config struct**: `FluxConfig` with `Provider`, `APIKey`, `BaseURL`, `Model`, `MaxRetries` fields
71-
- **Provider implementations**: `AnthropicClient`, `OpenAIClient`, `GeminiClient`, `BedrockClient`, etc. — in `client/` package
72-
- **Compatibility configs**: `OpenAICompat`, `GrokCompat`, `OpenRouterCompat` — `Compat` suffix for provider quirks
68+
- **Provider interface**: `provider/core.Provider` with `Chat()`, `StreamChat()`, `Ping()`, `Name()`
69+
- **Core request types**: `provider/core.FluxMessage`, `FluxResponse`, `FluxTool`, `FluxUsage`
70+
- **Config struct**: `provider/core.FluxConfig` with `Provider`, `APIKey`, `BaseURL`, `Model`, `MaxRetries`
71+
- **Provider implementations**: `provider/adapters/AnthropicClient`, `OpenAIClient`, `GeminiClient`, etc.
72+
- **Compatibility configs**: `provider/adapters.OpenAICompat`, `GrokCompat`, `OpenRouterCompat`
7373
- **Error type**: `FluxError` with `Provider`, `Op`, `StatusCode`, `RequestID`, `Message`, `Err` fields
7474
- **Stream types**: `StreamResult`, `SSEEvent`, `StreamEvent` — streaming is SSE-based
7575
- **Retry config**: `RetryConfig` embeds `types.RetryConfig` + adds `RetryOn []int` for HTTP status codes
76-
- **Version wiring**: `client.Version` set via `SetVersion()` from root package — avoids circular import
76+
- **Version wiring**: `provider.Version` set via `SetVersion()` from root package — avoids circular import
7777

7878
## API Patterns
7979

8080
- **Provider auto-detection**: `DetectProvider()` checks env vars in priority order (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.)
81-
- **Client creation**: `client.NewFluxClient(&FluxConfig{...})` or `client.Client(&FluxConfig{...})` — both work
81+
- **Client creation**: `provider.NewFluxClient(&core.FluxConfig{...})` or `provider.Client(&core.FluxConfig{...})`
8282
- **Chat method**: `c.Chat(ctx, messages, opts)` — non-streaming, returns `*FluxResponse`
8383
- **Stream method**: `c.StreamChat(ctx, messages, opts)` — returns `*StreamResult`, caller must `defer sr.Close()`
8484
- **Auto-continuation**: `StreamChatContinue()` transparently retries when `stop_reason == max_tokens`
@@ -111,38 +111,38 @@ make ci # Full CI suite
111111
- **Do not touch**: `Provider` interface (`Chat`, `StreamChat`, `Ping`, `Name`) — breaking change for all implementations
112112
- **Do not touch**: `FluxMessage`, `FluxResponse`, `ChatOptions` struct field names — serialization contract
113113
- **Do not touch**: `FluxError` struct — used by consumers for error type assertions
114-
- **Do not touch**: `client.FluxConfig` — constructor contract for all consumers
114+
- **Do not touch**: `provider/core.FluxConfig` — constructor contract for all consumers
115115
- **Safe to extend**: add new provider implementations, new SSE event types, new cache strategies
116-
- **When adding a provider**: create `client/<provider>.go`, implement `Provider` interface, register in `provider_registry.go`
116+
- **When adding a provider**: create `provider/<provider>.go`, implement `Provider` interface, register in `provider_registry.go`
117117

118118
## Key File Locations
119119

120120
| What | Where |
121121
|---|---|
122-
| Provider interface | `client/client.go` (`Provider`, `FluxConfig`, `FluxMessage`, `ContentPart`) |
123-
| Chat implementation | `client/chat.go` (`Chat()`, `StreamChat()`, `StreamChatContinue()`) |
122+
| Provider interface | `provider/core/core.go` (`Provider`, `FluxConfig`, `FluxMessage`, `ContentPart`) |
123+
| Chat implementation | `provider/chat.go` (`Chat()`, `StreamChat()`, `StreamChatContinue()`) |
124124
| Host-facing engine facade | `engine/` |
125-
| Provider-neutral core | `client/core/` |
126-
| Anthropic provider | `client/adapters/anthropic.go` |
127-
| OpenAI provider | `client/adapters/openai.go` |
128-
| Gemini provider | `client/adapters/gemini.go` |
129-
| Bedrock provider | `client/adapters/bedrock.go` |
130-
| Vertex provider | `client/adapters/vertex.go` |
131-
| Azure provider | `client/adapters/azure.go` |
132-
| Provider registry | `client/adapters/provider_registry.go` |
133-
| Provider compatibility | `client/adapters/compat.go` (`OpenAICompat`, `GrokCompat`, etc.) |
134-
| SSE streaming | `client/stream.go` (`parseSSEStream()`, `SSEEvent`) |
135-
| Retry logic | `client/retry.go` (`RetryConfig`, `backoffDelay()`, `shouldRetry()`) |
136-
| Rate limiting | `client/ratelimit.go`, `client/adaptive_ratelimit.go` |
137-
| Caching | `client/cache.go`, `client/semantic_cache.go`, `client/cache_analytics.go` |
138-
| Fallback chains | `client/fallback.go` |
139-
| Auto-continuation | `client/continuation.go` |
140-
| Error types | `client/errors.go` (`FluxError`, `IsRetriable()`, `IsAuthError()`) |
125+
| Provider-neutral core | `provider/core/` |
126+
| Anthropic provider | `provider/adapters/anthropic.go` |
127+
| OpenAI provider | `provider/adapters/openai.go` |
128+
| Gemini provider | `provider/adapters/gemini.go` |
129+
| Bedrock provider | `provider/adapters/bedrock.go` |
130+
| Vertex provider | `provider/adapters/vertex.go` |
131+
| Azure provider | `provider/adapters/azure.go` |
132+
| Provider registry | `provider/adapters/provider_registry.go` |
133+
| Provider compatibility | `provider/adapters/compat.go` (`OpenAICompat`, `GrokCompat`, etc.) |
134+
| SSE streaming | `provider/stream.go` (`parseSSEStream()`, `SSEEvent`) |
135+
| Retry logic | `provider/core/retry.go` (`RetryConfig`, `backoffDelay()`, `shouldRetry()`) |
136+
| Rate limiting | `provider/resilience/ratelimit.go`, `provider/resilience/adaptive_ratelimit.go` |
137+
| Caching | `provider/cache/cache.go`, `provider/cache/semantic_cache.go` |
138+
| Fallback chains | `provider/resilience/fallback.go` |
139+
| Auto-continuation | `provider/resilience/continuation.go` |
140+
| Error types | `provider/errors.go` (`FluxError`, `IsRetriable()`, `IsAuthError()`) |
141141
| Error constants | `errors/errors.go` (API error messages, prompt-too-long parsing) |
142142
| Model catalog | `catalog/` (pricing, context windows, capabilities per provider) |
143143
| Credentials | `credentials/` (key storage, env detection, scrubbing) — `HasSecret` is silent on miss (boolean predicate); `LookupSecret` logs `Debug` on `ErrNotFound` and `Warn` on real backend errors |
144-
| Mock provider | `client/mock.go` |
145-
| Main test file | `client/client_test.go` (httptest servers, provider detection) |
144+
| Mock provider | `provider/testkit/mock.go` |
145+
| Main test file | `provider/client_test.go` (httptest servers, provider detection) |
146146
| Linter config | `.golangci.yml` (govet, ineffassign, misspell — minimal) |
147147

148148
This is an independent repository consumed by Rho. In the local

‎CHANGELOG.md‎

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,24 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · Versioning:
77

88
## [Unreleased]
99

10+
### Added
11+
- Versioned instance-local deployment routing and signed peer-manifest
12+
replication. Replicas validate whole snapshots, resolve credentials locally,
13+
and continue serving the last valid route during refresh failures.
14+
- Strict explicit-state replica constructor in `runtime`; no process-global
15+
credential lookup is required for the replicated route.
16+
- Client-owned OpenAI-compatible provider registration through
17+
`FluxClient.RegisterCustomProvider`.
18+
19+
### Fixed
20+
- Circuit breakers now admit at most one concurrent half-open probe and do
21+
not reserve probes during route filtering.
22+
1023
### Changed
24+
- Removed process-global custom gateway and dynamic provider registration,
25+
the ambient `OPENAI_API_BASE` auto-registration path, and no-op API-key
26+
prefix inference. Custom gateways and endpoints now require explicit,
27+
instance-local configuration.
1128
- **Renamed host product references to rho.** Host config paths (`~/.rho/`),
1229
the `rho_build`/`rho_build_concise` tool namespaces, the `rho_response`
1330
schema name, and the `EXPORT_RHO_FIXTURE` env var now use the rho identity.
@@ -34,7 +51,7 @@ v0.0.1 of `github.com/GrayCodeAI/flux`.
3451

3552
### Changed — Shared MiMo auth-retry helper (2026-08-16)
3653
- **Deduplicated `doRequestWithMimoAuthRetry`** between the OpenAI and
37-
Anthropic adapters into one `doWithMimoAuthRetry` helper (client/adapters,
54+
Anthropic adapters into one `doWithMimoAuthRetry` helper (provider/adapters,
3855
next to `mimoAuthHeaders`); the two adapters now differ only in the Bearer
3956
headers they apply to the 401 retry. No behavior change.
4057

@@ -47,7 +64,7 @@ v0.0.1 of `github.com/GrayCodeAI/flux`.
4764

4865
### Fixed — Non-fatal stream diagnostics no longer fail the stream (2026-08-16)
4966
- **Stream health diagnostics are now warnings, not terminal errors.**
50-
`client/core`'s OpenAI stream processor emits end-of-stream diagnostics
67+
`provider/core`'s OpenAI stream processor emits end-of-stream diagnostics
5168
(reasoning-only responses, empty responses) as error-type events followed
5269
by the terminal `done` — but the engine mapped *every* error event to
5370
`provider_unavailable`, stopped forwarding, and set `Err()` even though
@@ -100,7 +117,7 @@ v0.0.1 of `github.com/GrayCodeAI/flux`.
100117

101118
### Changed
102119
- **Version re-baselined to `0.1.0`** in `flux.go` (`const Version`) and
103-
`client/client.go` (`var Version`, used in the `User-Agent` header).
120+
`provider/provider.go` (`var Version`, used in the `User-Agent` header).
104121

105122
### Added — Round 2 ecosystem improvements (2026-06-01)
106123
- **`internal/shrink`** package: tool-description shrink for LLM tool
@@ -126,9 +143,9 @@ v0.0.1 of `github.com/GrayCodeAI/flux`.
126143
### Added — Production Hardening (top-50 OSS parity)
127144
- Same-style hardening pass already on this branch:
128145
strict `golangci-lint` v2 config, unchecked-error fixes across
129-
`observability.go`, `sdk/go/client.go`, `storage/dag.go`,
146+
`observability.go`, `sdk/go/provider.go`, `storage/dag.go`,
130147
`storage/sqlite.go`, dead-code removal, and gofmt cleanup of the
131-
residual blank-line drift in `client/client.go`.
148+
residual blank-line drift in `provider/provider.go`.
132149
- `CONTRIBUTING.md` — development setup, branch flow, conventional
133150
commits, test/lint requirements.
134151
- `CODE_OF_CONDUCT.md` — Contributor Covenant 2.1.

‎Makefile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ GOVULNCHECK := $(GOBIN_DIR)/govulncheck
3434

3535
boundaries: ## Enforce support-repo import boundaries.
3636
bash ./scripts/check-ecosystem-boundaries.sh
37-
bash ./scripts/check-client-layering.sh
37+
bash ./scripts/check-provider-layering.sh
3838

3939
.PHONY: check-replace
4040
check-replace: ## Fail if go.mod has local replace directives (run before tagging)

0 commit comments

Comments
 (0)