Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0b71b25
Make all eyrie provider implementations uniform
Patel230 Jul 12, 2026
d9c78c0
WIP: client package decomposition and provider uniformity work
Patel230 Jul 12, 2026
bbe18ec
feat: add host-neutral engine facade
Patel230 Jul 12, 2026
01d7259
feat: complete engine state and continuation support
Patel230 Jul 12, 2026
02960a1
build: enforce host-neutral eyrie boundary
Patel230 Jul 12, 2026
ff4c04d
feat: complete engine generation contract
Patel230 Jul 12, 2026
afefd9b
fix: list models by serving gateway
Patel230 Jul 12, 2026
21f23df
fix: preserve credential save reassurance
Patel230 Jul 12, 2026
3e78f08
feat: expose engine selection lifecycle
Patel230 Jul 12, 2026
2188c98
fix: preserve model-only selection compatibility
Patel230 Jul 12, 2026
ae09dfb
feat: move native compaction behind engine
Patel230 Jul 12, 2026
a46f5d9
feat: expose provider control plane
Patel230 Jul 12, 2026
7067245
feat: expose effective engine selection
Patel230 Jul 12, 2026
205b5f9
fix: require configured local gateway
Patel230 Jul 12, 2026
5a55a67
feat: complete host model metadata
Patel230 Jul 12, 2026
3257aea
feat: expose host-neutral model policy
Patel230 Jul 12, 2026
e057b52
fix: preserve deterministic model metadata
Patel230 Jul 12, 2026
bb1f2be
feat: expose host control diagnostics
Patel230 Jul 12, 2026
1d82742
feat: report complete catalog health
Patel230 Jul 12, 2026
a47e56b
fix: expose credential env aliases
Patel230 Jul 12, 2026
6b394d7
feat: complete host control facade
Patel230 Jul 12, 2026
ed1a154
feat: close engine runtime boundary
Patel230 Jul 12, 2026
60597f1
feat: harden host engine boundary
Patel230 Jul 12, 2026
e60f089
refactor: enforce client adapter layering
Patel230 Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ jobs:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Boundary guard
run: bash ./scripts/check-ecosystem-boundaries.sh
run: |
bash ./scripts/check-ecosystem-boundaries.sh
bash ./scripts/check-client-layering.sh
- name: gofumpt
run: |
go install mvdan.cc/gofumpt@v0.10.0
Expand Down Expand Up @@ -121,7 +123,9 @@ jobs:
- name: Clone tok (workspace dep)
run: git clone --depth=1 https://github.com/GrayCodeAI/tok.git ../tok
- name: Boundary guard
run: bash ./scripts/check-ecosystem-boundaries.sh
run: |
bash ./scripts/check-ecosystem-boundaries.sh
bash ./scripts/check-client-layering.sh
- name: Run golangci-lint
run: |
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.0
Expand All @@ -143,7 +147,9 @@ jobs:
- name: Clone tok (workspace dep)
run: git clone --depth=1 https://github.com/GrayCodeAI/tok.git ../tok
- name: Boundary guard
run: bash ./scripts/check-ecosystem-boundaries.sh
run: |
bash ./scripts/check-ecosystem-boundaries.sh
bash ./scripts/check-client-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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/basic
bin/
*.exe
*.test

# Secrets / local config
.env
Expand All @@ -11,6 +12,8 @@ bin/

# Build / cache / seed artifacts
.gocache/
.gomodcache/
.golangci-cache/
*.seeds.json
elrond*.db
*.codegraph.db
Expand Down
40 changes: 23 additions & 17 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Universal LLM provider runtime. One interface for every model. Authentication, r
## Design Principles

- **Model-agnostic** — single interface for 75+ LLM providers
- **Zero opinions** — consumers control routing, caching, and retry strategies
- **Host-neutral engine** — Eyrie 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
Expand All @@ -25,13 +26,13 @@ make ci # Full CI suite

## Architecture

- `provider.go` — Provider interface and registry
- `routing.go` — Model routing and fallback chains
- `streaming.go` — SSE streaming with backpressure
- `auth.go` — API key management and rotation
- `cache.go` — Response caching (optional)
- `retry.go` — Retry with exponential backoff + Retry-After
- `catalog.go` — Model catalog and capability discovery
- `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
- `credentials/` — API key storage, lookup, and safe status projection
- `catalog/` — model catalog, discovery, capabilities, and pricing
- `router/` and `runtime/` — route policy and runtime resolution

## Conventions

Expand All @@ -44,7 +45,10 @@ make ci # Full CI suite

## Common Pitfalls

- Provider interface is the boundary — keep it stable
- `engine` is Hawk's product boundary; Hawk must not assemble lower-level
`client`, `catalog`, `config`, `credentials`, `router`, or `runtime` packages
- `client.Provider` remains the lower-level compatibility boundary for other
consumers; preserve its method set and the facade's type identity
- Streaming tests need careful goroutine management
- `go.work` here should stay minimal; hawk's own `go.work` adds an `external/eyrie` replace so hawk can develop against a local eyrie checkout. Do not add extra local `replace` directives here without coordinating with hawk's workspace.

Expand Down Expand Up @@ -106,14 +110,16 @@ make ci # Full CI suite
|---|---|
| Provider interface | `client/client.go` (`Provider`, `EyrieConfig`, `EyrieMessage`, `ContentPart`) |
| Chat implementation | `client/chat.go` (`Chat()`, `StreamChat()`, `StreamChatContinue()`) |
| Anthropic provider | `client/anthropic.go` |
| OpenAI provider | `client/openai.go` |
| Gemini provider | `client/gemini.go` |
| Bedrock provider | `client/bedrock.go` |
| Vertex provider | `client/vertex.go` |
| Azure provider | `client/azure.go` |
| Provider registry | `client/provider_registry.go` |
| Provider compatibility | `client/compat.go` (`OpenAICompat`, `GrokCompat`, etc.) |
| 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` |
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Canonical hawk-eco Makefile for Go LIBRARY repos.
# eyrie is a library consumed by hawk (no standalone binary, no release).
# eyrie is a versioned Go library consumed by Hawk (no standalone binary).

# ---------------------------------------------------------------------------
# Project metadata
Expand Down Expand Up @@ -36,6 +36,7 @@ GOVULNCHECK := $(GOBIN_DIR)/govulncheck

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

# ---------------------------------------------------------------------------
# Default target.
Expand Down
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ When your app calls a model, eyrie figures out which provider to use, how to tal

**Your app never talks to an LLM API directly. eyrie does.**

Hawk is the product face: it owns UX, agent orchestration, tools, permissions,
sessions, and product semantics. Eyrie is the provider engine: it owns
credentials, catalog and route resolution, provider transports, normalized
streams, retry/fallback, usage, and provider telemetry. Hawk integrates through
the stable [`engine`](engine/) facade rather than assembling Eyrie's internal
provider packages.

## Ecosystem Boundaries

eyrie is a Hawk support engine. Keep the dependency edge one-way:
Expand Down Expand Up @@ -240,7 +247,11 @@ config.SaveProviderConfig(cfg, "") // save changes

```
eyrie/
├── client/ # Provider client & streaming interface
├── engine/ # Stable host-facing facade and provider-neutral DTOs
├── client/ # Backwards-compatible public client facade
│ ├── core/ # Provider-neutral wire, stream, retry, and transport primitives
│ ├── adapters/ # Provider protocol adapters and construction registry
│ └── embeddings/ # Embedding clients, cache, and defaults
├── config/ # Provider configuration & routing
│ └── credential/ # Credential file management
├── catalog/ # Model catalog & tier system
Expand All @@ -253,7 +264,7 @@ eyrie/
├── credentials/ # Credential management
├── docs/ # Documentation & guides
├── examples/ # Runnable code examples
├── router/ # Weighted provider router
├── router/ # Provider routing strategies
├── runtime/ # Runtime manifest & routing policies
├── storage/ # SQLite conversation DAG store
├── types/ # Branded types & API errors
Expand Down
18 changes: 14 additions & 4 deletions catalog/discover/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ func liveDeploymentIDs(v1Catalog catalog.Catalog) []string {
// Options configures catalog discovery: published catalog + live provider APIs via API keys.
type Options struct {
catalog.LoadCatalogOptions
Credentials catalog.Credentials
Credentials catalog.Credentials
DisableCredentialFallback bool
}

// Run loads the deployment-aware catalog, optionally refreshes the remote catalog,
Expand Down Expand Up @@ -100,13 +101,13 @@ func run(ctx context.Context, opts Options) (*catalog.RefreshResult, error) {
catalog.EnsureCredentialRegistryInCatalog(base)

env := opts.Credentials.Env()
if len(env) == 0 {
if len(env) == 0 && !opts.DisableCredentialFallback {
env = eyriecfg.DiscoveryCredentials(ctx).Env()
}
if len(env) > 0 {
v1Catalog, enrichment := catalog.FetchLiveProviderCatalog(env)
liveProviders = enrichment
if len(v1Catalog.Models) > 0 {
if liveEnrichmentSucceeded(enrichment) {
base = MergeCatalogWithPolicy(base, &v1Catalog, MergePolicy{
PreferLive: true,
ReplaceDeploymentOfferings: liveDeploymentIDs(v1Catalog),
Expand Down Expand Up @@ -137,9 +138,18 @@ func run(ctx context.Context, opts Options) (*catalog.RefreshResult, error) {
CachePath: opts.CachePath,
Source: source,
RemoteURL: opts.RemoteURL,
Refreshed: refreshed || len(liveProviders) > 0,
Refreshed: refreshed || liveEnrichmentSucceeded(liveProviders),
RemoteRefreshed: remoteRefreshed,
LiveProviders: liveProviders,
StaleAfter: base.StaleAfter,
}, nil
}

func liveEnrichmentSucceeded(enrichment []catalog.LiveProviderEnrichment) bool {
for _, provider := range enrichment {
if provider.Error == "" && provider.ModelCount > 0 {
return true
}
}
return false
}
Loading
Loading