Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
27 changes: 23 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,32 @@ FLUX_API_KEY=
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
GEMINI_API_KEY=
DEEPSEEK_API_KEY=
GROQ_API_KEY=
KIMI_API_KEY=
MOONSHOT_API_KEY=
ZAI_API_KEY=
ZAI_CODING_API_KEY=
XIAOMI_MIMO_PAYG_API_KEY=
XIAOMI_MIMO_TOKEN_PLAN_API_KEY=
MINIMAX_API_KEY=
MINIMAX_TOKEN_PLAN_API_KEY=
AZURE_OPENAI_API_KEY=
AWS_SECRET_ACCESS_KEY=
VERTEX_ACCESS_TOKEN=
OPENROUTER_API_KEY=
CONCENTRATE_API_KEY=
OPENGATEWAY_API_KEY=
STEPFUN_API_KEY=
AGNES_API_KEY=
LONGCAT_API_KEY=
FIREWORKS_API_KEY=
CANOPYWAVE_API_KEY=
POOLSIDE_API_KEY=
CLINE_API_KEY=
OPENCODEGO_API_KEY=
XAI_API_KEY=
ZAI_API_KEY=
OLLAMA_BASE_URL=

# Default model overrides
OPENAI_MODEL=gpt-4o
Expand All @@ -24,8 +46,5 @@ GEMINI_MODEL=gemini-2.0-flash
# FLUX_MODEL_CATALOG_PATH=
# FLUX_MODEL_CATALOG_REFRESH=

# OpenAI-compatible dynamic provider opt-in (1/true/yes)
# FLUX_ALLOW_DYNAMIC_PROVIDERS=

# Local config/state directory
FLUX_CONFIG_DIR=~/.flux
14 changes: 7 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
- name: Boundary guard
run: |
bash ./scripts/check-ecosystem-boundaries.sh
bash ./scripts/check-client-layering.sh
bash ./scripts/check-provider-layering.sh
- name: gofumpt
run: |
go install mvdan.cc/gofumpt@v0.10.0
Expand Down Expand Up @@ -128,7 +128,7 @@ jobs:
- name: Boundary guard
run: |
bash ./scripts/check-ecosystem-boundaries.sh
bash ./scripts/check-client-layering.sh
bash ./scripts/check-provider-layering.sh
- name: Run golangci-lint
run: |
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.0
Expand All @@ -150,7 +150,7 @@ jobs:
- name: Boundary guard
run: |
bash ./scripts/check-ecosystem-boundaries.sh
bash ./scripts/check-client-layering.sh
bash ./scripts/check-provider-layering.sh
- name: Test with race detector
run: go test ./... -race -count=1 -shuffle=on -coverprofile=coverage.out -covermode=atomic -timeout=300s
- name: Coverage summary
Expand Down Expand Up @@ -266,10 +266,10 @@ jobs:
cache: true
- name: Run fuzz targets
run: |
go test -fuzz=FuzzSanitizeMessages -fuzztime=60s ./client
go test -fuzz=FuzzMergeConsecutiveRoles -fuzztime=60s ./client
go test -fuzz=FuzzBuildCacheKey -fuzztime=60s ./client
go test -fuzz=FuzzGuardrailsCheck -fuzztime=60s ./client
go test -fuzz=FuzzSanitizeMessages -fuzztime=60s ./provider
go test -fuzz=FuzzMergeConsecutiveRoles -fuzztime=60s ./provider
go test -fuzz=FuzzBuildCacheKey -fuzztime=60s ./provider
go test -fuzz=FuzzGuardrailsCheck -fuzztime=60s ./provider

# -------------------------------------------------------------------------
# 10. Cross-platform build matrix — zero CGO, all targets.
Expand Down
70 changes: 35 additions & 35 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ make ci # Full CI suite
## Architecture

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

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

## Naming Conventions

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

## API Patterns

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

## Key File Locations

| What | Where |
|---|---|
| Provider interface | `client/client.go` (`Provider`, `FluxConfig`, `FluxMessage`, `ContentPart`) |
| Chat implementation | `client/chat.go` (`Chat()`, `StreamChat()`, `StreamChatContinue()`) |
| Provider interface | `provider/core/core.go` (`Provider`, `FluxConfig`, `FluxMessage`, `ContentPart`) |
| Chat implementation | `provider/chat.go` (`Chat()`, `StreamChat()`, `StreamChatContinue()`) |
| Host-facing engine facade | `engine/` |
| Provider-neutral core | `client/core/` |
| Anthropic provider | `client/adapters/anthropic.go` |
| OpenAI provider | `client/adapters/openai.go` |
| Gemini provider | `client/adapters/gemini.go` |
| Bedrock provider | `client/adapters/bedrock.go` |
| Vertex provider | `client/adapters/vertex.go` |
| Azure provider | `client/adapters/azure.go` |
| Provider registry | `client/adapters/provider_registry.go` |
| Provider compatibility | `client/adapters/compat.go` (`OpenAICompat`, `GrokCompat`, etc.) |
| SSE streaming | `client/stream.go` (`parseSSEStream()`, `SSEEvent`) |
| Retry logic | `client/retry.go` (`RetryConfig`, `backoffDelay()`, `shouldRetry()`) |
| Rate limiting | `client/ratelimit.go`, `client/adaptive_ratelimit.go` |
| Caching | `client/cache.go`, `client/semantic_cache.go`, `client/cache_analytics.go` |
| Fallback chains | `client/fallback.go` |
| Auto-continuation | `client/continuation.go` |
| Error types | `client/errors.go` (`FluxError`, `IsRetriable()`, `IsAuthError()`) |
| Provider-neutral core | `provider/core/` |
| Anthropic provider | `provider/adapters/anthropic.go` |
| OpenAI provider | `provider/adapters/openai.go` |
| Gemini provider | `provider/adapters/gemini.go` |
| Bedrock provider | `provider/adapters/bedrock.go` |
| Vertex provider | `provider/adapters/vertex.go` |
| Azure provider | `provider/adapters/azure.go` |
| Provider registry | `provider/adapters/provider_registry.go` |
| Provider compatibility | `provider/adapters/compat.go` (`OpenAICompat`, `GrokCompat`, etc.) |
| SSE streaming | `provider/stream.go` (`parseSSEStream()`, `SSEEvent`) |
| Retry logic | `provider/core/retry.go` (`RetryConfig`, `backoffDelay()`, `shouldRetry()`) |
| Rate limiting | `provider/resilience/ratelimit.go`, `provider/resilience/adaptive_ratelimit.go` |
| Caching | `provider/cache/cache.go`, `provider/cache/semantic_cache.go` |
| Fallback chains | `provider/resilience/fallback.go` |
| Auto-continuation | `provider/resilience/continuation.go` |
| Error types | `provider/errors.go` (`FluxError`, `IsRetriable()`, `IsAuthError()`) |
| Error constants | `errors/errors.go` (API error messages, prompt-too-long parsing) |
| Model catalog | `catalog/` (pricing, context windows, capabilities per provider) |
| 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 |
| Mock provider | `client/mock.go` |
| Main test file | `client/client_test.go` (httptest servers, provider detection) |
| Mock provider | `provider/testkit/mock.go` |
| Main test file | `provider/client_test.go` (httptest servers, provider detection) |
| Linter config | `.golangci.yml` (govet, ineffassign, misspell — minimal) |

This is an independent repository consumed by Rho. In the local
Expand Down
27 changes: 22 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,24 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · Versioning:

## [Unreleased]

### Added
- Versioned instance-local deployment routing and signed peer-manifest
replication. Replicas validate whole snapshots, resolve credentials locally,
and continue serving the last valid route during refresh failures.
- Strict explicit-state replica constructor in `runtime`; no process-global
credential lookup is required for the replicated route.
- Client-owned OpenAI-compatible provider registration through
`FluxClient.RegisterCustomProvider`.

### Fixed
- Circuit breakers now admit at most one concurrent half-open probe and do
not reserve probes during route filtering.

### Changed
- Removed process-global custom gateway and dynamic provider registration,
the ambient `OPENAI_API_BASE` auto-registration path, and no-op API-key
prefix inference. Custom gateways and endpoints now require explicit,
instance-local configuration.
- **Renamed host product references to rho.** Host config paths (`~/.rho/`),
the `rho_build`/`rho_build_concise` tool namespaces, the `rho_response`
schema name, and the `EXPORT_RHO_FIXTURE` env var now use the rho identity.
Expand All @@ -34,7 +51,7 @@ v0.0.1 of `github.com/GrayCodeAI/flux`.

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

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

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

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

### Added — Round 2 ecosystem improvements (2026-06-01)
- **`internal/shrink`** package: tool-description shrink for LLM tool
Expand All @@ -126,9 +143,9 @@ v0.0.1 of `github.com/GrayCodeAI/flux`.
### Added — Production Hardening (top-50 OSS parity)
- Same-style hardening pass already on this branch:
strict `golangci-lint` v2 config, unchecked-error fixes across
`observability.go`, `sdk/go/client.go`, `storage/dag.go`,
`observability.go`, `sdk/go/provider.go`, `storage/dag.go`,
`storage/sqlite.go`, dead-code removal, and gofmt cleanup of the
residual blank-line drift in `client/client.go`.
residual blank-line drift in `provider/provider.go`.
- `CONTRIBUTING.md` — development setup, branch flow, conventional
commits, test/lint requirements.
- `CODE_OF_CONDUCT.md` — Contributor Covenant 2.1.
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ GOVULNCHECK := $(GOBIN_DIR)/govulncheck

boundaries: ## Enforce support-repo import boundaries.
bash ./scripts/check-ecosystem-boundaries.sh
bash ./scripts/check-client-layering.sh
bash ./scripts/check-provider-layering.sh

.PHONY: check-replace
check-replace: ## Fail if go.mod has local replace directives (run before tagging)
Expand Down
Loading
Loading