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 @@
# graycode-router environment variables — copy to .env and fill in
GRAYCODE_ROUTER_API_KEY=
GRAYCODE_ROUTER_ALLOW_INSECURE_PUBLIC_API=false
# eyrie environment variables — copy to .env and fill in
EYRIE_API_KEY=
EYRIE_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=~/.graycode-router
EYRIE_CONFIG_DIR=~/.eyrie
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# CODEOWNERS for graycode-router (code-generation engine)
# CODEOWNERS for eyrie (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/graycode-router/issues) to avoid duplicates.
- Search [existing issues](https://github.com/GrayCodeAI/eyrie/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 `graycode-routerClient.Chat(...)` against <provider>, I expected X but got Y.
placeholder: When I call `eyrieClient.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 := graycode-router.New(...)
resp, err := client.Chat(ctx, msgs, graycode-router.ChatOptions{Model: "..."})
client := eyrie.New(...)
resp, err := client.Chat(ctx, msgs, eyrie.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: graycode-router-version
id: eyrie-version
attributes:
label: graycode-router version
description: Output of `graycode-router version` or the `graycode-router.Version` constant / git SHA you built from.
label: eyrie version
description: Output of `eyrie version` or the `eyrie.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/graycode-router/security/advisories/new
url: https://github.com/GrayCodeAI/eyrie/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/graycode-router/discussions
url: https://github.com/GrayCodeAI/eyrie/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. graycode-router is the LLM client + skill router
Thanks for proposing a feature. eyrie 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/graycode-router/issues) to avoid duplicates.
- Search [existing issues](https://github.com/GrayCodeAI/eyrie/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, graycode-router makes me do Y, which costs Z extra tokens / a goroutine leak / ...
placeholder: When I'm doing X, eyrie 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 graycode-router to behave? Snippet of API / config / CLI you'd want.
description: How would you like eyrie 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: graycode-router avoids enterprise scope. Confirm this feature respects that.
description: eyrie 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 graycode-router (Go library — no binaries).
# Release workflow for eyrie (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/graycode-router@vX.Y.Z`.
# `go get github.com/GrayCodeAI/eyrie@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
/graycode-router
/eyrie
/basic
bin/
*.exe
Expand Down
40 changes: 20 additions & 20 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# AGENTS.md — GraycodeRouter
# AGENTS.md — Eyrie

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

Expand All @@ -9,13 +9,13 @@ 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** — GraycodeRouter owns provider routing, transport, caching,
- **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

See [graycode/docs/OTEL-CONVENTIONS.md](https://github.com/GrayCodeAI/graycode-cli/blob/main/docs/OTEL-CONVENTIONS.md) for the shared OpenTelemetry attribute vocabulary (`gen_ai.*`, `cost.usd`, etc.) used across all GrayCodeAI repos.
See [hawk/docs/OTEL-CONVENTIONS.md](https://github.com/GrayCodeAI/hawk/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 Down Expand Up @@ -49,44 +49,44 @@ make ci # Full CI suite

## Common Pitfalls

- `engine`, `llm`, `graph` and `tools` are the host contract surface. Graycode
- `engine`, `llm`, `graph` and `tools` are the host contract surface. Hawk
must not assemble `client`, `catalog`, `config`, `credentials`, `router` or
`runtime`. Six symbols Graycode needs (`ChatOptions`, `ContinuationConfig`,
`runtime`. Six symbols Hawk 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
- Streaming tests need careful goroutine management
- `go.work` here should stay minimal; the parent `graycode-eco/go.work`
connects this independent `graycode-router` checkout beside Graycode for local development.
connects this independent `eyrie` 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**: `GraycodeRouterClient`, `GraycodeRouterMessage`, `GraycodeRouterResponse`, `GraycodeRouterTool`, `GraycodeRouterUsage` — `GraycodeRouter` prefix for public types
- **Config struct**: `GraycodeRouterConfig` with `Provider`, `APIKey`, `BaseURL`, `Model`, `MaxRetries` fields
- **Client types**: `EyrieClient`, `EyrieMessage`, `EyrieResponse`, `EyrieTool`, `EyrieUsage` — `Eyrie` prefix for public types
- **Config struct**: `EyrieConfig` 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**: `GraycodeRouterError` with `Provider`, `Op`, `StatusCode`, `RequestID`, `Message`, `Err` fields
- **Error type**: `EyrieError` 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.NewGraycodeRouterClient(&GraycodeRouterConfig{...})` or `client.Client(&GraycodeRouterConfig{...})` — both work
- **Chat method**: `c.Chat(ctx, messages, opts)` — non-streaming, returns `*GraycodeRouterResponse`
- **Client creation**: `client.NewEyrieClient(&EyrieConfig{...})` or `client.Client(&EyrieConfig{...})` — both work
- **Chat method**: `c.Chat(ctx, messages, opts)` — non-streaming, returns `*EyrieResponse`
- **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**: `GraycodeRouterError` has `IsRetriable()`, `IsAuthError()`, `IsRateLimited()` methods for programmatic handling
- **Error hierarchy**: `EyrieError` 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**: `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
- **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
- **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`, `GraycodeRouterConfig`, `GraycodeRouterMessage`, `ContentPart`) |
| Provider interface | `client/client.go` (`Provider`, `EyrieConfig`, `EyrieMessage`, `ContentPart`) |
| Chat implementation | `client/chat.go` (`Chat()`, `StreamChat()`, `StreamChatContinue()`) |
| Host-facing engine facade | `engine/` |
| Provider-neutral core | `client/core/` |
Expand All @@ -137,15 +137,15 @@ 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` (`GraycodeRouterError`, `IsRetriable()`, `IsAuthError()`) |
| Error types | `client/errors.go` (`EyrieError`, `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) |
| Linter config | `.golangci.yml` (govet, ineffassign, misspell — minimal) |

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