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
  •  
  •  
  •  
8 changes: 4 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# eyrie environment variables — copy to .env and fill in
EYRIE_API_KEY=
EYRIE_ALLOW_INSECURE_PUBLIC_API=false
# graycode-router environment variables — copy to .env and fill in
GRAYCODE_ROUTER_API_KEY=
GRAYCODE_ROUTER_ALLOW_INSECURE_PUBLIC_API=false
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
GEMINI_API_KEY=
Expand All @@ -11,4 +11,4 @@ ZAI_API_KEY=
OPENAI_MODEL=gpt-4o
ANTHROPIC_MODEL=claude-sonnet-4-5
GEMINI_MODEL=gemini-2.0-flash
HAWK_CONFIG_DIR=~/.eyrie
HAWK_CONFIG_DIR=~/.graycode-router
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 graycode-router (code-generation engine)
* @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/eyrie/issues) to avoid duplicates.
- Search [existing issues](https://github.com/GrayCodeAI/graycode-router/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 `graycode-routerClient.Chat(...)` against <provider>, I expected X but got Y.
validations:
required: true

Expand All @@ -30,8 +30,8 @@ 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 := graycode-router.New(...)
resp, err := client.Chat(ctx, msgs, graycode-router.ChatOptions{Model: "..."})
// ^ panics / returns wrong shape / hangs / etc.
validations:
required: true
Expand Down Expand Up @@ -68,10 +68,10 @@ body:
placeholder: "claude-opus-4-20250514"

- type: input
id: eyrie-version
id: graycode-router-version
attributes:
label: eyrie version
description: Output of `eyrie version` or the `eyrie.Version` constant / git SHA you built from.
label: graycode-router version
description: Output of `graycode-router version` or the `graycode-router.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/graycode-router/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/graycode-router/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. graycode-router 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/graycode-router/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, graycode-router 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 graycode-router 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: graycode-router 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 graycode-router (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/graycode-router@vX.Y.Z`.

name: release

Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Binaries
/eyrie
/graycode-router
/basic
bin/
*.exe
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 — GraycodeRouter

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** — GraycodeRouter 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 @@ -55,34 +55,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 `graycode-router` 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**: `GraycodeRouterClient`, `GraycodeRouterMessage`, `GraycodeRouterResponse`, `GraycodeRouterTool`, `GraycodeRouterUsage` — `GraycodeRouter` prefix for public types
- **Config struct**: `GraycodeRouterConfig` 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**: `GraycodeRouterError` 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.NewGraycodeRouterClient(&GraycodeRouterConfig{...})` or `client.Client(&GraycodeRouterConfig{...})` — both work
- **Chat method**: `c.Chat(ctx, messages, opts)` — non-streaming, returns `*GraycodeRouterResponse`
- **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**: `GraycodeRouterError` 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 @@ -105,17 +105,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**: `GraycodeRouterMessage`, `GraycodeRouterResponse`, `ChatOptions` struct field names — serialization contract
- **Do not touch**: `GraycodeRouterError` struct — used by consumers for error type assertions
- **Do not touch**: `client.GraycodeRouterConfig` — 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`, `GraycodeRouterConfig`, `GraycodeRouterMessage`, `ContentPart`) |
| Chat implementation | `client/chat.go` (`Chat()`, `StreamChat()`, `StreamChatContinue()`) |
| Host-facing engine facade | `engine/` |
| Provider-neutral core | `client/core/` |
Expand All @@ -133,7 +133,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` (`GraycodeRouterError`, `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 @@ -142,6 +142,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 `../graycode-router`
and connected through the parent `go.work`; publish changes here, then update
Hawk's module pin through a separate PR.
42 changes: 25 additions & 17 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
# Changelog

All notable changes to eyrie are documented here.
All notable changes to graycode-router are documented here.
Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · Versioning: [SemVer](https://semver.org/)

---

## [0.0.1] — 2026-09-04

### Changed — Renamed eyrie to graycode-router
- **Module is now `github.com/GrayCodeAI/graycode-router`.** All `Eyrie*`
types are `GraycodeRouter*`, `EYRIE_*` env vars are `GRAYCODE_ROUTER_*`,
and config lives under `~/.graycode-router`. Breaking rename with no
behavior change; prior history is preserved below.

## [Unreleased]

### Changed — Shared MiMo auth-retry helper (2026-08-16)
Expand All @@ -27,7 +35,7 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · Versioning:
by the terminal `done` — but the engine mapped *every* error event to
`provider_unavailable`, stopped forwarding, and set `Err()` even though
content had been delivered. Diagnostic events are now marked non-fatal via
the existing `EyrieStreamEvent.Warning` field (additive); the engine
the existing `GraycodeRouterStreamEvent.Warning` field (additive); the engine
forwards them as `warning` events and still delivers the final
`done`/usage event with `Err()` unset. Genuinely fatal stream errors keep
the previous behavior. The deprecated client continuation helper and the
Expand All @@ -43,7 +51,7 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · Versioning:
stream paths) on 429/500/502/503/529 with backoff and `Retry-After`
support; `SetRetry` previously discarded the config with a comment claiming
the HTTP client handled retries (it never does).
- **Concentrate errors are structured `*core.EyrieError`s** built by
- **Concentrate errors are structured `*core.GraycodeRouterError`s** built by
`core.ParseProviderError`/`core.FormatAPIError` (8KB bounded read,
provider/op/status/request-ID preserved), so `IsRetriable()`/`IsAuthError()`
and the engine's error classification work; the captured `X-Request-Id` is
Expand All @@ -58,7 +66,7 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · Versioning:
Actions and exchanges it for AWS Bedrock (STS `AssumeRoleWithWebIdentity`) or
GCP Vertex (Workload Identity Federation) credentials, no stored secrets.
- **OpenAI-compatible proxy** — `POST /v1/chat/completions` endpoint so existing
OpenAI SDK clients can talk to eyrie unchanged.
OpenAI SDK clients can talk to graycode-router unchanged.
- **Named load-balancing strategies** — `simple-shuffle`, `least-busy`,
`latency-based`, `cost-based`, and `usage-based` alongside the default
weighted router.
Expand All @@ -74,7 +82,7 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · Versioning:
build tag, ready to wire when generated stubs are available.

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

### Added — Round 2 ecosystem improvements (2026-06-01)
Expand All @@ -90,7 +98,7 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · Versioning:
5. Whitespace collapse
Reports aggregate `BytesSaved` and `PercentOff` across all tools
in the slice. Safe to call concurrently.
Aligns eyrie with the rest of the hawk-eco ecosystem (`hawk`, `shrike`,
Aligns graycode-router with the rest of the hawk-eco ecosystem (`hawk`, `shrike`,
`harrier`, `kestrel`, `merlin`).

### Added
Expand Down Expand Up @@ -120,16 +128,16 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · Versioning:
issues.

### Fixed (2026-05-16)
- Flaky test `TestOpenAIStreamChat_MultipleToolCalls` order assertion ([f5e7f3b](https://github.com/GrayCodeAI/eyrie/commit/f5e7f3b21d092886971b092027a8184c6fd8a385))
- gofumpt formatting ([7de54e3](https://github.com/GrayCodeAI/eyrie/commit/7de54e352170e456bbfe9bfee9b79e244210db72))
- gofumpt formatting + `go mod tidy` ([8bd58dc](https://github.com/GrayCodeAI/eyrie/commit/8bd58dcea346eb9df8d13d9020d936df2ceba1b5))
- Remaining errcheck issues ([a0a746a](https://github.com/GrayCodeAI/eyrie/commit/a0a746aa95cd9acdf5d29ef836985e13ab864ba1))
- Resolve all lint errors (bodyclose, errcheck, gocritic, noctx, unlambda) ([566980f](https://github.com/GrayCodeAI/eyrie/commit/566980f39bdd7ebba072ce4c17bc19bc0f3cf2a0))
- Syntax errors in test files ([51809bc](https://github.com/GrayCodeAI/eyrie/commit/51809bcb181a27eb344a1493501f59c593b3065b))
- Upgrade Go from 1.26.1 to 1.26.3 to patch stdlib vulnerabilities ([f4a6594](https://github.com/GrayCodeAI/eyrie/commit/f4a65944a6f777947071c9b870a6f2c4f74891be))
- Flaky test `TestOpenAIStreamChat_MultipleToolCalls` order assertion ([f5e7f3b](https://github.com/GrayCodeAI/graycode-router/commit/f5e7f3b21d092886971b092027a8184c6fd8a385))
- gofumpt formatting ([7de54e3](https://github.com/GrayCodeAI/graycode-router/commit/7de54e352170e456bbfe9bfee9b79e244210db72))
- gofumpt formatting + `go mod tidy` ([8bd58dc](https://github.com/GrayCodeAI/graycode-router/commit/8bd58dcea346eb9df8d13d9020d936df2ceba1b5))
- Remaining errcheck issues ([a0a746a](https://github.com/GrayCodeAI/graycode-router/commit/a0a746aa95cd9acdf5d29ef836985e13ab864ba1))
- Resolve all lint errors (bodyclose, errcheck, gocritic, noctx, unlambda) ([566980f](https://github.com/GrayCodeAI/graycode-router/commit/566980f39bdd7ebba072ce4c17bc19bc0f3cf2a0))
- Syntax errors in test files ([51809bc](https://github.com/GrayCodeAI/graycode-router/commit/51809bcb181a27eb344a1493501f59c593b3065b))
- Upgrade Go from 1.26.1 to 1.26.3 to patch stdlib vulnerabilities ([f4a6594](https://github.com/GrayCodeAI/graycode-router/commit/f4a65944a6f777947071c9b870a6f2c4f74891be))

### Tests (2026-05-16)
- Fix flaky `TestBackoffDelay` by accounting for jitter ([5fa573f](https://github.com/GrayCodeAI/eyrie/commit/5fa573fe3ec8074a84bf81d10ea59837bf40b7bc))
- Fix flaky `TestBackoffDelay` by accounting for jitter ([5fa573f](https://github.com/GrayCodeAI/graycode-router/commit/5fa573fe3ec8074a84bf81d10ea59837bf40b7bc))

## [0.1.0] — 2026-05-12

Expand All @@ -142,7 +150,7 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · Versioning:
- `AnthropicClient` — Anthropic Messages API with full content block support
- `OpenAIClient` — OpenAI and all OpenAI-compatible providers
- `MockProvider` — testing without API keys (echo / fixed / tool_use / error / max_tokens modes)
- `EyrieClient` — thread-safe universal client with cached provider instances
- `GraycodeRouterClient` — thread-safe universal client with cached provider instances

**Streaming**
- SSE parser for Anthropic and OpenAI formats
Expand Down Expand Up @@ -187,8 +195,8 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · Versioning:
- Embedding support

**Quality**
- `User-Agent: eyrie/0.1.0` on all HTTP requests
- `User-Agent: graycode-router/0.1.0` on all HTTP requests
- Request ID captured from response headers
- 4 KB error body cap — prevents OOM on large error responses
- `"eyrie: "` prefix on all errors with `%w` wrapping
- `"graycode-router: "` prefix on all errors with `%w` wrapping
- Zero external dependencies · Go 1.26
Loading
Loading