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
  •  
  •  
  •  
25 changes: 21 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
# eyrie environment variables — copy to .env and fill in
EYRIE_API_KEY=
EYRIE_ALLOW_INSECURE_PUBLIC_API=false
# flux environment variables — copy to .env and fill in

# Provider API keys (or store them in the OS keychain — see
# docs/guides/CREDENTIAL-SETUP-FLOW.md)
FLUX_API_KEY=
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
GEMINI_API_KEY=
OPENROUTER_API_KEY=
CANOPYWAVE_API_KEY=
XAI_API_KEY=
ZAI_API_KEY=

# Default model overrides
OPENAI_MODEL=gpt-4o
ANTHROPIC_MODEL=claude-sonnet-4-5
GEMINI_MODEL=gemini-2.0-flash
EYRIE_CONFIG_DIR=~/.eyrie

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

# Model catalog (dynamic discovery)
# FLUX_MODEL_CATALOG_URL=
# 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
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# CODEOWNERS for eyrie (code-generation engine)
# CODEOWNERS for flux (universal LLM provider runtime)
* @GrayCodeAI/maintainers

# Engine core
Expand Down
16 changes: 9 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/eyrie/issues) to avoid duplicates.
- Search [existing issues](https://github.com/GrayCodeAI/flux/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 `eyrieClient.Chat(...)` against <provider>, I expected X but got Y.
placeholder: When I call `FluxClient.Chat(...)` against <provider>, I expected X but got Y.
validations:
required: true

Expand All @@ -30,8 +30,10 @@ body:
description: Minimal Go snippet (or curl/CLI invocation) that reliably reproduces the problem.
render: go
placeholder: |
client := eyrie.New(...)
resp, err := client.Chat(ctx, msgs, eyrie.ChatOptions{Model: "..."})
client := client.Client(&client.FluxConfig{
Provider: client.DetectProvider(),
})
resp, err := client.Chat(ctx, msgs, client.ChatOptions{Model: "..."})
// ^ panics / returns wrong shape / hangs / etc.
validations:
required: true
Expand Down Expand Up @@ -68,10 +70,10 @@ body:
placeholder: "claude-opus-4-20250514"

- type: input
id: eyrie-version
id: flux-version
attributes:
label: eyrie version
description: Output of `eyrie version` or the `eyrie.Version` constant / git SHA you built from.
label: flux version
description: Output of `flux version` or the `flux.Version` constant / git SHA you built from.
placeholder: "0.1.0"
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/eyrie/security/advisories/new
url: https://github.com/GrayCodeAI/flux/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/eyrie/discussions
url: https://github.com/GrayCodeAI/flux/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. eyrie is the LLM client + skill router
Thanks for proposing a feature. flux is the LLM client + skill router
that backs the hawk-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/eyrie/issues) to avoid duplicates.
- Search [existing issues](https://github.com/GrayCodeAI/flux/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, eyrie makes me do Y, which costs Z extra tokens / a goroutine leak / ...
placeholder: When I'm doing X, flux 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 eyrie to behave? Snippet of API / config / CLI you'd want.
description: How would you like flux 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: eyrie avoids enterprise scope. Confirm this feature respects that.
description: flux 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 hawk-eco service.
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Release workflow for eyrie (Go library — no binaries).
# Release workflow for flux (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/eyrie@vX.Y.Z`.
# `go get github.com/GrayCodeAI/flux@vX.Y.Z`.

name: release

Expand Down
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Binaries
/eyrie
/basic
/flux
bin/
*.exe
*.test
Expand Down
30 changes: 15 additions & 15 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# AGENTS.md — Eyrie
# AGENTS.md — Flux

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

Expand All @@ -9,7 +9,7 @@ When starting any new work (feature, fix, refactor, chore), always create a feat
## Design Principles

- **Model-agnostic** — single interface for 75+ LLM providers
- **Host-neutral engine** — Eyrie owns provider routing, transport, caching,
- **Host-neutral engine** — Flux 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

Expand Down Expand Up @@ -59,34 +59,34 @@ make ci # Full CI suite
consumers; preserve its method set and the facade's type identity
- Streaming tests need careful goroutine management
- `go.work` here should stay minimal; the parent `graycode-eco/go.work`
connects this independent `eyrie` checkout beside Hawk for local development.
connects this independent `flux` checkout beside Hawk for local development.
Do not add extra local `replace` directives here without coordinating with
the parent workspace.

## Naming Conventions

- **Provider interface**: `client.Provider` with `Chat()`, `StreamChat()`, `Ping()`, `Name()` — implemented per LLM vendor
- **Client types**: `EyrieClient`, `EyrieMessage`, `EyrieResponse`, `EyrieTool`, `EyrieUsage` — `Eyrie` prefix for public types
- **Config struct**: `EyrieConfig` with `Provider`, `APIKey`, `BaseURL`, `Model`, `MaxRetries` fields
- **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
- **Error type**: `EyrieError` with `Provider`, `Op`, `StatusCode`, `RequestID`, `Message`, `Err` fields
- **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

## API Patterns

- **Provider auto-detection**: `DetectProvider()` checks env vars in priority order (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.)
- **Client creation**: `client.NewEyrieClient(&EyrieConfig{...})` or `client.Client(&EyrieConfig{...})` — both work
- **Chat method**: `c.Chat(ctx, messages, opts)` — non-streaming, returns `*EyrieResponse`
- **Client creation**: `client.NewFluxClient(&FluxConfig{...})` or `client.Client(&FluxConfig{...})` — both work
- **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`
- **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**: `EyrieError` has `IsRetriable()`, `IsAuthError()`, `IsRateLimited()` methods for programmatic handling
- **Error hierarchy**: `FluxError` 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 @@ -109,17 +109,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**: `EyrieMessage`, `EyrieResponse`, `ChatOptions` struct field names — serialization contract
- **Do not touch**: `EyrieError` struct — used by consumers for error type assertions
- **Do not touch**: `client.EyrieConfig` — constructor contract for all consumers
- **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
- **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`

## Key File Locations

| What | Where |
|---|---|
| Provider interface | `client/client.go` (`Provider`, `EyrieConfig`, `EyrieMessage`, `ContentPart`) |
| Provider interface | `client/client.go` (`Provider`, `FluxConfig`, `FluxMessage`, `ContentPart`) |
| Chat implementation | `client/chat.go` (`Chat()`, `StreamChat()`, `StreamChatContinue()`) |
| Host-facing engine facade | `engine/` |
| Provider-neutral core | `client/core/` |
Expand All @@ -137,7 +137,7 @@ make ci # Full CI suite
| 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` (`EyrieError`, `IsRetriable()`, `IsAuthError()`) |
| Error types | `client/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 |
Expand All @@ -146,6 +146,6 @@ make ci # Full CI suite
| Linter config | `.golangci.yml` (govet, ineffassign, misspell — minimal) |

This is an independent repository consumed by Hawk. In the local
`graycode-eco` parent workspace it is checked out beside `hawk` as `../eyrie`
`graycode-eco` parent workspace it is checked out beside `hawk` as `../flux`
and connected through the parent `go.work`; publish changes here, then update
Hawk's module pin through a separate PR.
Loading
Loading