diff --git a/README.md b/README.md index 7b49b02..c60152a 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,8 @@ provider packages. eyrie is a Hawk support engine. Keep the dependency edge one-way: -- eyrie uses local-only types (provider/transport types are eyrie-scoped, not shared contracts) +- host-facing DTOs and the `Provider` port live in `hawk-core-contracts/llm`; `engine/` re-exports them as aliases (`*Engine` implements `llm.Provider`) +- internal provider/transport types stay eyrie-scoped (not shared contracts) - do not import `hawk/internal/*` - do not import removed legacy path `hawk/shared/types` - do not import other engines (`yaad`, `tok`, `trace`, `sight`, `inspect`) — engines are peers, not dependencies diff --git a/engine/compaction.go b/engine/compaction.go index f8b5c55..b6d29b4 100644 --- a/engine/compaction.go +++ b/engine/compaction.go @@ -6,17 +6,6 @@ import ( "github.com/GrayCodeAI/eyrie/runtime" ) -// NativeCompactionRequest asks Eyrie to use a provider-native compaction -// protocol without exposing provider credentials to the host. -type NativeCompactionRequest struct { - Provider string - Model string - Messages []Message - ContextWindow int - ThresholdPct int - MaxOutputTokens int -} - // SupportsNativeCompaction reports whether the selection and configured // credential store support native compaction. func (e *Engine) SupportsNativeCompaction(ctx context.Context, provider, model string) bool { diff --git a/engine/contract_assert.go b/engine/contract_assert.go new file mode 100644 index 0000000..6d4e275 --- /dev/null +++ b/engine/contract_assert.go @@ -0,0 +1,12 @@ +package engine + +import "github.com/GrayCodeAI/hawk-core-contracts/llm" + +// Compile-time assertions: the host facade implements the shared port. +// If a method is added to llm.Provider (or EventStreamer), this file fails +// to compile until Engine/Stream grow matching methods — catching drift early. +var ( + _ llm.Provider = (*Engine)(nil) + _ llm.EventStreamer = (*Stream)(nil) + _ llm.Generator = (*Engine)(nil) +) diff --git a/engine/doc.go b/engine/doc.go index d77ff76..3915022 100644 --- a/engine/doc.go +++ b/engine/doc.go @@ -7,4 +7,8 @@ // Engine is intentionally stateless with respect to product conversations: // the host owns conversation history, tools, permissions, and checkpoints; // Eyrie owns credential, catalog, selection, routing, and model transport. +// +// Host-facing DTOs and the Provider port live in +// github.com/GrayCodeAI/hawk-core-contracts/llm; this package re-exports them +// as type aliases and *Engine implements llm.Provider (see contract_assert.go). package engine diff --git a/engine/engine.go b/engine/engine.go index 4a8bf0c..72d199d 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -198,8 +198,22 @@ func (e *Engine) Generate(ctx context.Context, req GenerateRequest) (*GenerateRe } // Stream starts a normalized streaming generation. The returned stream must be -// closed by the caller. -func (e *Engine) Stream(ctx context.Context, req GenerateRequest) (*Stream, error) { +// closed by the caller. The concrete type is *Stream; the signature returns +// EventStreamer so *Engine satisfies llm.Provider / llm.Generator. +// +// When the concrete *Stream is nil, this returns a typed-nil EventStreamer +// (interface nil) so callers can check stream == nil reliably. +func (e *Engine) Stream(ctx context.Context, req GenerateRequest) (EventStreamer, error) { + s, err := e.stream(ctx, req) + if s == nil { + return nil, err + } + return s, err +} + +// stream is the concrete implementation used by Stream and by callers that need +// the *Stream type without a type assertion. +func (e *Engine) stream(ctx context.Context, req GenerateRequest) (*Stream, error) { ctx = nonNilContext(ctx) if err := validateGenerateRequest(req); err != nil { return nil, err diff --git a/engine/host_control.go b/engine/host_control.go index 150bf71..e263d84 100644 --- a/engine/host_control.go +++ b/engine/host_control.go @@ -234,20 +234,6 @@ func (e *Engine) ApplyGatewayEnvironment(_ context.Context, providerID string) { } } -type CatalogHealth struct { - Path string `json:"path"` - Exists bool `json:"exists"` - ModifiedAt time.Time `json:"modified_at,omitempty"` - Size int64 `json:"size,omitempty"` - Models int `json:"models,omitempty"` - Deployments int `json:"deployments,omitempty"` - Offerings int `json:"offerings,omitempty"` - Stale bool `json:"stale,omitempty"` - StaleAfter time.Time `json:"stale_after,omitempty"` - Source string `json:"source,omitempty"` - Error string `json:"error,omitempty"` -} - func (e *Engine) CatalogHealth(ctx context.Context) CatalogHealth { health := CatalogHealth{Path: e.catalogPath} exists, modified, size, err := catalog.CacheInfo(e.catalogPath) @@ -316,12 +302,6 @@ func (e *Engine) DeploymentStatus(ctx context.Context, activeModel string) (stri return setup.FormatStatus(report), nil } -type DeploymentSummary struct { - RoutingSource string `json:"routing_source,omitempty"` - RoutingStages int `json:"routing_stages,omitempty"` - Formatted string `json:"formatted"` -} - func (e *Engine) DeploymentSummary(ctx context.Context, activeModel string) (DeploymentSummary, error) { report, err := setup.DeploymentStatusFromPaths(nonNilContext(ctx), activeModel, e.providerConfigPath, e.catalogPath) if err != nil { @@ -366,12 +346,6 @@ func (e *Engine) Preflight(ctx context.Context) PreflightReport { return e.PreflightWithOptions(ctx, PreflightOptions{}) } -// PreflightOptions controls whether readiness remains a local state check or -// also verifies the selected provider over the network. -type PreflightOptions struct { - VerifyLive bool `json:"verify_live,omitempty"` -} - func (e *Engine) PreflightWithOptions(ctx context.Context, opts PreflightOptions) PreflightReport { ctx = nonNilContext(ctx) var checks []PreflightCheck @@ -523,26 +497,6 @@ func providerModelAvailable(compiled *catalog.CompiledCatalog, providerID, model return false } -type CheckStatus string - -const ( - CheckOK CheckStatus = "ok" - CheckWarn CheckStatus = "warn" - CheckFail CheckStatus = "fail" -) - -type PreflightCheck struct { - Name string `json:"name"` - Status CheckStatus `json:"status"` - Detail string `json:"detail"` -} - -type PreflightReport struct { - Ready bool `json:"ready"` - LiveVerified bool `json:"live_verified"` - Checks []PreflightCheck `json:"checks"` -} - func FormatPreflight(report PreflightReport) string { var b strings.Builder switch { @@ -566,13 +520,6 @@ func FormatPreflight(report PreflightReport) string { return strings.TrimRight(b.String(), "\n") } -type ProviderStateSecurity struct { - Path string `json:"path"` - HasSecrets bool `json:"has_secrets"` - Detail string `json:"detail,omitempty"` - Error string `json:"error,omitempty"` -} - // ProviderStateSecurityStatus inspects provider.json without returning values. func (e *Engine) ProviderStateSecurityStatus() ProviderStateSecurity { status := ProviderStateSecurity{Path: e.providerConfigPath} diff --git a/engine/types.go b/engine/types.go index a4573a6..599cb78 100644 --- a/engine/types.go +++ b/engine/types.go @@ -113,3 +113,40 @@ type Selection = llm.Selection // SelectionOptions contains optional user or command-line overrides. type SelectionOptions = llm.SelectionOptions + +// CatalogHealth is the host-facing catalog health report. +type CatalogHealth = llm.CatalogHealth + +// DeploymentSummary is the host-facing deployment routing summary. +type DeploymentSummary = llm.DeploymentSummary + +// PreflightOptions configures a readiness check. +type PreflightOptions = llm.PreflightOptions + +// PreflightReport is the host-facing preflight result. +type PreflightReport = llm.PreflightReport + +// PreflightCheck is one readiness check row. +type PreflightCheck = llm.PreflightCheck + +// CheckStatus is a preflight check status. +type CheckStatus = llm.CheckStatus + +// ProviderStateSecurity reports provider.json security posture (no secrets). +type ProviderStateSecurity = llm.ProviderStateSecurity + +// NativeCompactionRequest is a provider-native compaction request. +type NativeCompactionRequest = llm.NativeCompactionRequest + +// EventStreamer is the pull-based host stream contract. +type EventStreamer = llm.EventStreamer + +// Provider is the full host port (composition of role interfaces in llm). +type Provider = llm.Provider + +// Re-export preflight status constants from the contract package. +const ( + CheckOK = llm.CheckOK + CheckFail = llm.CheckFail + CheckWarn = llm.CheckWarn +) diff --git a/go.mod b/go.mod index 8418bef..aa9abf2 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/GrayCodeAI/eyrie go 1.26.5 require ( - github.com/GrayCodeAI/hawk-core-contracts v0.1.7 + github.com/GrayCodeAI/hawk-core-contracts v0.1.8 github.com/google/uuid v1.6.0 github.com/tiktoken-go/tokenizer v0.8.0 github.com/zalando/go-keyring v0.2.8 diff --git a/go.sum b/go.sum index dbc26f4..78e324e 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/GrayCodeAI/hawk-core-contracts v0.1.7 h1:dLU+TagRNmqWg3qOUsrBls6VD8GcBBYs7FcdHNUOU1Y= -github.com/GrayCodeAI/hawk-core-contracts v0.1.7/go.mod h1:BXbh68YrCf+s9HVqND5F8DAvl2MnE5NcOwZZZB56HGA= +github.com/GrayCodeAI/hawk-core-contracts v0.1.8 h1:SkDsGZJXL+3DYG0Fi3NXvNe/NlhP/KZn+Feofnx35Zc= +github.com/GrayCodeAI/hawk-core-contracts v0.1.8/go.mod h1:BXbh68YrCf+s9HVqND5F8DAvl2MnE5NcOwZZZB56HGA= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ=