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
  •  
  •  
  •  
12 changes: 6 additions & 6 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# flux environment variables — copy to .env and fill in
# mux environment variables — copy to .env and fill in

# Provider credentials, one per registry entry in catalog/registry/providers.go
# (registry SortOrder). Or store them in the OS keychain — see
Expand Down Expand Up @@ -39,12 +39,12 @@ ANTHROPIC_MODEL=claude-sonnet-4-5
GEMINI_MODEL=gemini-2.0-flash

# Optional: inject custom HTTP headers into every provider request
# FLUX_CUSTOM_HEADERS="X-Custom: value1\nX-Other: value2"
# MUX_CUSTOM_HEADERS="X-Custom: value1\nX-Other: value2"

# Model catalog (dynamic discovery)
# FLUX_MODEL_CATALOG_URL=
# FLUX_MODEL_CATALOG_PATH=
# FLUX_MODEL_CATALOG_REFRESH=
# MUX_MODEL_CATALOG_URL=
# MUX_MODEL_CATALOG_PATH=
# MUX_MODEL_CATALOG_REFRESH=

# Local config/state directory
FLUX_CONFIG_DIR=~/.flux
MUX_CONFIG_DIR=~/.mux
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# CODEOWNERS for flux (universal LLM provider runtime)
# CODEOWNERS for mux (universal LLM provider runtime)
* @GrayCodeAI/maintainers

# Engine core
Expand Down
14 changes: 7 additions & 7 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ body:
of the form as you can — the more we know, the faster we can fix it.

Before submitting:
- Search [existing issues](https://github.com/GrayCodeAI/flux/issues) to avoid duplicates.
- Search [existing issues](https://github.com/GrayCodeAI/mux/issues) to avoid duplicates.
- If this is a security issue, please **do not** file a public issue. See `SECURITY.md`.

- type: textarea
id: what-happened
attributes:
label: What happened?
description: A clear, concise description of the bug.
placeholder: When I call `FluxClient.Chat(...)` against <provider>, I expected X but got Y.
placeholder: When I call `MuxClient.Chat(...)` against <provider>, I expected X but got Y.
validations:
required: true

Expand All @@ -30,7 +30,7 @@ body:
description: Minimal Go snippet (or curl/CLI invocation) that reliably reproduces the problem.
render: go
placeholder: |
client := client.Client(&client.FluxConfig{
client := client.Client(&client.MuxConfig{
Provider: client.DetectProvider(),
})
resp, err := client.Chat(ctx, msgs, client.ChatOptions{Model: "..."})
Expand Down Expand Up @@ -70,10 +70,10 @@ body:
placeholder: "claude-opus-4-20250514"

- type: input
id: flux-version
id: mux-version
attributes:
label: flux version
description: Output of `flux version` or the `flux.Version` constant / git SHA you built from.
label: mux version
description: Output of `mux version` or the `mux.Version` constant / git SHA you built from.
placeholder: "0.1.0"
validations:
required: true
Expand All @@ -83,7 +83,7 @@ body:
attributes:
label: Go version
description: Output of `go version`.
placeholder: "go version go1.26.1 darwin/arm64"
placeholder: "go version go1.27.2 darwin/arm64"
validations:
required: true

Expand Down
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Security vulnerability
url: https://github.com/GrayCodeAI/flux/security/advisories/new
url: https://github.com/GrayCodeAI/mux/security/advisories/new
about: Please report security issues privately via a GitHub Security Advisory. See SECURITY.md.
- name: Question / discussion
url: https://github.com/GrayCodeAI/flux/discussions
url: https://github.com/GrayCodeAI/mux/discussions
about: Have a question or want to discuss an idea? Open a discussion instead of an issue.
10 changes: 5 additions & 5 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ body:
- type: markdown
attributes:
value: |
Thanks for proposing a feature. flux is the LLM client + skill router
Thanks for proposing a feature. mux is the LLM client + skill router
that backs the graycode-eco ecosystem. Every feature is evaluated against
whether it serves **a single developer** running an AI agent locally.

Before submitting:
- Search [existing issues](https://github.com/GrayCodeAI/flux/issues) to avoid duplicates.
- Search [existing issues](https://github.com/GrayCodeAI/mux/issues) to avoid duplicates.
- For new providers, please open a discussion first — adding a provider
is a long-term maintenance commitment.

Expand All @@ -39,15 +39,15 @@ body:
attributes:
label: What problem are you trying to solve?
description: Describe the user problem first. Solutions can come later.
placeholder: When I'm doing X, flux makes me do Y, which costs Z extra tokens / a goroutine leak / ...
placeholder: When I'm doing X, mux makes me do Y, which costs Z extra tokens / a goroutine leak / ...
validations:
required: true

- type: textarea
id: proposal
attributes:
label: Proposed solution
description: How would you like flux to behave? Snippet of API / config / CLI you'd want.
description: How would you like mux to behave? Snippet of API / config / CLI you'd want.
render: go
validations:
required: true
Expand All @@ -62,7 +62,7 @@ body:
id: principles
attributes:
label: Developer fit
description: flux avoids enterprise scope. Confirm this feature respects that.
description: mux avoids enterprise scope. Confirm this feature respects that.
options:
- label: Works with zero configuration (sensible defaults).
- label: Does not require a network call to a graycode-eco service.
Expand Down
27 changes: 21 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ concurrency:
cancel-in-progress: true

env:
GO_VERSION: "1.26.6"
GO_VERSION: "1.27.2"
GOPRIVATE: "github.com/GrayCodeAI/*"
GONOSUMDB: "github.com/GrayCodeAI/*"
GONOSUMCHECK: "1"
Expand All @@ -32,6 +32,13 @@ env:
GOWORK: "off"

jobs:
version:
name: Canonical version metadata
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- run: python3 scripts/sync-version.py --check

# -------------------------------------------------------------------------
# 1. Format — gofumpt + goimports must be clean (zero tolerance).
# -------------------------------------------------------------------------
Expand Down Expand Up @@ -59,7 +66,7 @@ jobs:
fi
- name: goimports
run: |
go install golang.org/x/tools/cmd/goimports@v0.30.0
go install golang.org/x/tools/cmd/goimports@v0.52.0
out=$(goimports -l .)
if [ -n "$out" ]; then
echo "::error::goimports would reformat:"
Expand Down Expand Up @@ -131,7 +138,7 @@ jobs:
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
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.14.0
golangci-lint run --timeout=5m

# -------------------------------------------------------------------------
Expand Down Expand Up @@ -186,11 +193,19 @@ jobs:
cache: true
- name: govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@v1.1.4
go install golang.org/x/vuln/cmd/govulncheck@v1.8.0
govulncheck ./...
- name: gosec (advisory)
run: |
go install github.com/securego/gosec/v2/cmd/gosec@v2.22.4
# gosec's release still pins an older Go export-data reader.
# Build it with Go 1.27-compatible x/tools in an isolated module.
tool_dir=$(mktemp -d)
(
cd "$tool_dir"
go mod init graycodeai.local/gosec-tool
go get github.com/securego/gosec/v2/cmd/gosec@v2.29.0 golang.org/x/tools@v0.52.0
go install github.com/securego/gosec/v2/cmd/gosec
)
gosec -exclude=G104,G703,G704,G101,G107,G112,G114,G115,G201,G202,G203,G204,G301,G302,G304,G305,G306,G307,G401,G402,G403,G404,G501,G502,G503,G504,G505,G601,G602 -confidence=medium -severity=high ./...

# -------------------------------------------------------------------------
Expand All @@ -207,7 +222,7 @@ jobs:
cache: true
- name: deadcode
run: |
go install golang.org/x/tools/cmd/deadcode@v0.30.0
go install golang.org/x/tools/cmd/deadcode@v0.52.0
deadcode -test ./...

# -------------------------------------------------------------------------
Expand Down
11 changes: 9 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Release workflow for flux (Go library — no binaries).
# Release workflow for mux (Go library — no binaries).
# Triggered when a v* tag is pushed; publishes a GitHub Release with
# auto-generated notes. Consumers depend on the tag via
# `go get github.com/GrayCodeAI/flux@vX.Y.Z`.
# `go get github.com/GrayCodeAI/mux@vX.Y.Z`.

name: release

Expand All @@ -21,6 +21,13 @@ jobs:
with:
fetch-depth: 0

- name: Verify canonical version metadata
run: python3 scripts/sync-version.py --check

- name: Verify tag matches VERSION
shell: bash
run: test "$GITHUB_REF_NAME" = "v$(tr -d '[:space:]' < VERSION)"

- name: Create GitHub Release
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
with:
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Binaries
/flux
/mux
bin/
*.exe
*.test
Expand Down
54 changes: 27 additions & 27 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# AGENTS.md — Flux
# AGENTS.md — Mux

Universal LLM provider runtime. One interface for every model. Authentication, routing, streaming, retries, caching — handled.

Expand All @@ -10,13 +10,13 @@ When starting any new work (feature, fix, refactor, chore), always create a feat

- **Model-agnostic** — single interface for the 28 provider gateways in
`catalog/registry/providers.go` (see README "Supported Providers")
- **Host-neutral engine** — Flux owns provider routing, transport, caching,
- **Host-neutral engine** — Mux owns provider routing, transport, caching,
retry/fallback, and normalized telemetry; hosts own product UX and semantics
- **Streaming-first** — all responses are streamed; blocking is opt-in

## Observability

See [rho/docs/OTEL-CONVENTIONS.md](https://github.com/GrayCodeAI/rho/blob/main/docs/OTEL-CONVENTIONS.md) for the shared OpenTelemetry attribute vocabulary (`gen_ai.*`, `cost.usd`, etc.) used across all GrayCodeAI repos.
See [decoder/docs/OTEL-CONVENTIONS.md](https://github.com/GrayCodeAI/decoder/blob/main/docs/OTEL-CONVENTIONS.md) for the shared OpenTelemetry attribute vocabulary (`gen_ai.*`, `cost.usd`, etc.) used across all GrayCodeAI repos.

## Build & Test

Expand All @@ -41,7 +41,7 @@ make ci # Full CI suite

## Conventions

- Go 1.26+, pure Go, no CGO
- Go 1.27.2+, pure Go, no CGO
- Table-driven tests
- Conventional Commits: `feat:`, `fix:`, `docs:`, `refactor:`, `test:`
- No `Co-authored-by:` trailers (auto-stripped by githook)
Expand All @@ -50,9 +50,9 @@ make ci # Full CI suite

## Common Pitfalls

- `engine`, `llm`, `graph` and `tools` are the host contract surface. Rho
- `engine`, `llm`, `graph` and `tools` are the host contract surface. Decoder
must not assemble `provider`, `catalog`, `config`, `credentials`, `router` or
`runtime`. Six symbols Rho needs (`ChatOptions`, `ContinuationConfig`,
`runtime`. Six symbols Decoder 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.
Expand All @@ -66,43 +66,43 @@ make ci # Full CI suite
method set stable and use it across feature packages
- Streaming tests need careful goroutine management
- `go.work` here should stay minimal; the `graycode-eco` workspace connects
this independent `flux` checkout beside Rho for local development.
this independent `mux` checkout beside Decoder for local development.
Do not add extra local `replace` directives here without coordinating with
the parent workspace.
- There is usually **no** `go.work` in the parent folder, and that is expected.
A parent `go.work` breaks every sibling it does not list, and exporting
`GOWORK` is worse: it is inherited by child `go` processes, so rho's own
`GOWORK` is worse: it is inherited by child `go` processes, so decoder's own
tests that shell out to `go test` in temporary projects fail. To exercise
rho against this checkout, use a gitignored module overlay in rho instead —
`go test -modfile=go.local.mod ./...`. See rho/AGENTS.md
"Workspace workflow" for the setup. Without that overlay, rho compiles
against the *published* flux from the module cache, so a green rho suite
does not exercise local flux changes at all.
decoder against this checkout, use a gitignored module overlay in decoder instead —
`go test -modfile=go.local.mod ./...`. See decoder/AGENTS.md
"Workspace workflow" for the setup. Without that overlay, decoder compiles
against the *published* mux from the module cache, so a green decoder suite
does not exercise local mux changes at all.

## Naming Conventions

- **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`
- **Core request types**: `provider/core.MuxMessage`, `MuxResponse`, `MuxTool`, `MuxUsage`
- **Config struct**: `provider/core.MuxConfig` with `Provider`, `APIKey`, `BaseURL`, `Model`, `MaxRetries`
- **Provider implementations**: `provider/adapters/anthropic.go` (`AnthropicClient`), `openai.go` (`OpenAIClient`), `gemini.go` (`GeminiClient`), etc.
- **Compatibility configs**: `provider/adapters.OpenAICompat`, `GrokCompat`, `OpenRouterCompat`
- **Error type**: `FluxError` with `Provider`, `Op`, `StatusCode`, `RequestID`, `Message`, `Err` fields
- **Error type**: `MuxError` 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**: `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**: `provider.NewFluxClient(&core.FluxConfig{...})` or `provider.Client(&core.FluxConfig{...})`
- **Chat method**: `c.Chat(ctx, messages, opts)` — non-streaming, returns `*FluxResponse`
- **Client creation**: `provider.NewMuxClient(&core.MuxConfig{...})` or `provider.Client(&core.MuxConfig{...})`
- **Chat method**: `c.Chat(ctx, messages, opts)` — non-streaming, returns `*MuxResponse`
- **Stream method**: `c.StreamChat(ctx, messages, opts)` — returns `*StreamResult`, caller must `defer sr.Close()`
- **Auto-continuation**: `StreamChatContinue()` transparently retries when `stop_reason == max_tokens`
- **Provider fallback**: `fallback.go` implements fallback chains across providers
- **Rate limiting**: `ratelimit.go` implements token bucket per provider — prevents hitting API limits
- **Semantic caching**: `semantic_cache.go` caches similar prompts — optional, configurable TTL
- **Retry with backoff**: `retry.go` — exponential backoff + jitter, respects `Retry-After` header, retries on 429/500/502/503/529
- **Error hierarchy**: `FluxError` has `IsRetriable()`, `IsAuthError()`, `IsRateLimited()` methods for programmatic handling
- **Error hierarchy**: `MuxError` has `IsRetriable()`, `IsAuthError()`, `IsRateLimited()` methods for programmatic handling
- **SSE parsing**: `parseSSEStream()` reads `bufio.Scanner` with 2MB buffer, emits `SSEEvent` to channel

## Testing Patterns
Expand All @@ -125,17 +125,17 @@ make ci # Full CI suite
- **Safe to refactor**: `fallback.go`, `weighted.go` — routing strategies, extend with new strategies
- **Safe to refactor**: `cost_estimator.go`, `cache_analytics.go` — metrics and tracking
- **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**: `provider/core.FluxConfig` — constructor contract for all consumers
- **Do not touch**: `MuxMessage`, `MuxResponse`, `ChatOptions` struct field names — serialization contract
- **Do not touch**: `MuxError` struct — used by consumers for error type assertions
- **Do not touch**: `provider/core.MuxConfig` — constructor contract for all consumers
- **Safe to extend**: add new provider implementations, new SSE event types, new cache strategies
- **When adding a provider**: create `provider/<provider>.go`, implement `Provider` interface, register in `provider_registry.go`

## Key File Locations

| What | Where |
|---|---|
| Provider interface | `provider/core/core.go` (`Provider`, `FluxConfig`, `FluxMessage`, `ContentPart`) |
| Provider interface | `provider/core/core.go` (`Provider`, `MuxConfig`, `MuxMessage`, `ContentPart`) |
| Chat implementation | `provider/chat.go` (`Chat()`, `StreamChat()`, `StreamChatContinue()`) |
| Host-facing engine facade | `engine/` |
| Provider-neutral core | `provider/core/` |
Expand All @@ -153,15 +153,15 @@ make ci # Full CI suite
| Caching | `provider/cache/cache.go`, `provider/cache/semantic_cache.go` |
| Fallback chains | `router/router.go` (fallback providers), `router/deployment_router.go` (fallback deployment stages) |
| Auto-continuation | `provider/resilience/continuation.go` |
| Error types | `provider/core/errors.go` (`FluxError`, `IsRetriable()`, `IsAuthError()`) |
| Error types | `provider/core/errors.go` (`MuxError`, `IsRetriable()`, `IsAuthError()`) |
| Error constants | `types/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 | `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
`graycode-eco` parent workspace it is checked out beside `rho` as `../flux`
This is an independent repository consumed by Decoder. In the local
`graycode-eco` parent workspace it is checked out beside `decoder` as `../mux`
and connected through the parent `go.work`; publish changes here, then update
Rho's module pin through a separate PR.
Decoder's module pin through a separate PR.
Loading
Loading