This commit is contained in:
@@ -34,9 +34,11 @@ type providerRegistration struct {
|
||||
}
|
||||
|
||||
type cognitiveGateway struct {
|
||||
resolver bindingResolver
|
||||
providers map[capability.ProviderKind][]providerRegistration
|
||||
now func() time.Time
|
||||
resolver bindingResolver
|
||||
providers map[capability.ProviderKind][]providerRegistration
|
||||
now func() time.Time
|
||||
allowCanary bool
|
||||
canaryMaxOutputBytes int
|
||||
}
|
||||
|
||||
func (g cognitiveGateway) policyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -44,7 +46,7 @@ func (g cognitiveGateway) policyHandler(w http.ResponseWriter, r *http.Request)
|
||||
if !decodeJSON(w, r, 8<<10, &request) || request.PrincipalID == "" {
|
||||
return
|
||||
}
|
||||
requirement := capability.CognitiveRequirement{CapabilityID: creatorAdvisoryCapabilityID, OwnerPrincipalID: request.PrincipalID, DataPolicy: request.DataPolicy, RequiredModalities: []capability.ModelModality{capability.ModalityText}, RequiredCapabilities: []capability.ModelCapability{capability.CapabilityAdvisory}, MaxOutputBytes: 1}
|
||||
requirement := capability.CognitiveRequirement{CapabilityID: creatorAdvisoryCapabilityID, OwnerPrincipalID: request.PrincipalID, DataPolicy: request.DataPolicy, RequiredModalities: []capability.ModelModality{capability.ModalityText}, RequiredCapabilities: []capability.ModelCapability{capability.CapabilityAdvisory}, AllowCanary: g.allowCanary, MaxOutputBytes: 1}
|
||||
selected, err := g.resolver.Resolve(r.Context(), requirement)
|
||||
policy := cognitive.GatewayAdvisoryPolicy{}
|
||||
if err == nil && selected.Binding.DataPolicy != capability.DataPolicyLocalOnly {
|
||||
@@ -65,7 +67,11 @@ func (g cognitiveGateway) advisoryHandler(w http.ResponseWriter, r *http.Request
|
||||
http.Error(w, "invalid advisory request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
requirement := capability.CognitiveRequirement{CapabilityID: creatorAdvisoryCapabilityID, OwnerPrincipalID: request.PrincipalID, DataPolicy: request.DataPolicy, RequiredModalities: []capability.ModelModality{capability.ModalityText}, RequiredCapabilities: []capability.ModelCapability{capability.CapabilityAdvisory}, InputBytes: int64(len(request.Message)), MaxOutputBytes: int64(request.MaxOutputBytes)}
|
||||
maxOutputBytes := request.MaxOutputBytes
|
||||
if g.allowCanary && g.canaryMaxOutputBytes > 0 && maxOutputBytes > g.canaryMaxOutputBytes {
|
||||
maxOutputBytes = g.canaryMaxOutputBytes
|
||||
}
|
||||
requirement := capability.CognitiveRequirement{CapabilityID: creatorAdvisoryCapabilityID, OwnerPrincipalID: request.PrincipalID, DataPolicy: request.DataPolicy, RequiredModalities: []capability.ModelModality{capability.ModalityText}, RequiredCapabilities: []capability.ModelCapability{capability.CapabilityAdvisory}, AllowCanary: g.allowCanary, InputBytes: int64(len(request.Message)), MaxOutputBytes: int64(maxOutputBytes)}
|
||||
selected, err := g.resolver.Resolve(r.Context(), requirement)
|
||||
if err != nil {
|
||||
writeGatewayResponse(w, cognitive.GatewayAdvisoryResponse{Version: request.Version, RequestID: request.RequestID, FinishReason: cognitive.FinishProviderUnavailable})
|
||||
@@ -80,7 +86,7 @@ func (g cognitiveGateway) advisoryHandler(w http.ResponseWriter, r *http.Request
|
||||
writeGatewayResponse(w, cognitive.GatewayAdvisoryResponse{Version: request.Version, RequestID: request.RequestID, FinishReason: cognitive.FinishProviderUnavailable})
|
||||
return
|
||||
}
|
||||
invocation := cognitive.AdvisoryRequest{Version: request.Version, RequestID: request.RequestID, ActorID: request.ActorID, PrincipalID: request.PrincipalID, ScopeID: request.ScopeID, Message: request.Message, Context: request.Context, Locale: request.Locale, MaxOutputBytes: request.MaxOutputBytes, ProviderBindingID: selected.Binding.ID, ProviderBindingRevision: selected.Binding.Revision, ProviderInstanceID: selected.Provider.ID, ModelDescriptorID: selected.Model.ID, ProviderModelID: selected.Model.ProviderModelID}
|
||||
invocation := cognitive.AdvisoryRequest{Version: request.Version, RequestID: request.RequestID, ActorID: request.ActorID, PrincipalID: request.PrincipalID, ScopeID: request.ScopeID, Message: request.Message, Context: request.Context, Locale: request.Locale, MaxOutputBytes: maxOutputBytes, ProviderBindingID: selected.Binding.ID, ProviderBindingRevision: selected.Binding.Revision, ProviderInstanceID: selected.Provider.ID, ModelDescriptorID: selected.Model.ID, ProviderModelID: selected.Model.ProviderModelID}
|
||||
started := g.now()
|
||||
response, invokeErr := provider.Advise(r.Context(), invocation)
|
||||
if invokeErr != nil {
|
||||
|
||||
@@ -87,6 +87,33 @@ func TestActiveRegistryBindingReachesProviderAdapter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanaryFlagIsForwardedToCapabilityResolver(t *testing.T) {
|
||||
registry := ®istryStub{err: errors.New("stop after resolve")}
|
||||
gateway := cognitiveGateway{resolver: registry, providers: map[capability.ProviderKind][]providerRegistration{}, now: time.Now, allowCanary: true}
|
||||
request := httptest.NewRequest(http.MethodPost, "/v1/advisories", strings.NewReader(gatewayRequest()))
|
||||
gateway.advisoryHandler(httptest.NewRecorder(), request)
|
||||
if !registry.requirement.AllowCanary {
|
||||
t.Fatal("server-side canary flag was not forwarded")
|
||||
}
|
||||
gateway.allowCanary = false
|
||||
gateway.advisoryHandler(httptest.NewRecorder(), httptest.NewRequest(http.MethodPost, "/v1/advisories", strings.NewReader(gatewayRequest())))
|
||||
if registry.requirement.AllowCanary {
|
||||
t.Fatal("canary was allowed by default")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanaryOutputLimitIsClamped(t *testing.T) {
|
||||
selected := gatewaySelection()
|
||||
selected.Binding.DataPolicy = capability.DataPolicyLocalOnly
|
||||
registry := ®istryStub{selected: selected}
|
||||
provider := &stubAdvisoryProvider{response: cognitive.AdvisoryResponse{Version: cognitive.CreatorAdvisoryV1, RequestID: "request-1", ProviderBindingID: selected.Binding.ID, ProviderBindingRevision: selected.Binding.Revision, AdvisoryText: "ok", FinishReason: cognitive.FinishCompleted}}
|
||||
gateway := cognitiveGateway{resolver: registry, providers: map[capability.ProviderKind][]providerRegistration{"provider-kind-1": {{EndpointRef: "endpoint-1", CredentialRef: "credential-1", Provider: provider}}}, now: time.Now, allowCanary: true, canaryMaxOutputBytes: 4096}
|
||||
gateway.advisoryHandler(httptest.NewRecorder(), httptest.NewRequest(http.MethodPost, "/v1/advisories", strings.NewReader(gatewayRequest())))
|
||||
if registry.requirement.MaxOutputBytes != 4096 || provider.request.MaxOutputBytes != 4096 {
|
||||
t.Fatalf("canary limit not clamped: requirement=%d invocation=%d", registry.requirement.MaxOutputBytes, provider.request.MaxOutputBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoActiveBindingKeepsGatewayAvailableWithoutInvocation(t *testing.T) {
|
||||
registry := ®istryStub{err: errors.New("no active binding")}
|
||||
provider := &stubAdvisoryProvider{}
|
||||
|
||||
@@ -58,7 +58,7 @@ func main() {
|
||||
kind := capability.ProviderKind(envOr("OPENAI_PROVIDER_KIND", "openai-responses-v1"))
|
||||
providers[kind] = append(providers[kind], providerRegistration{ProviderInstanceID: "openai-bootstrap", Kind: kind, EndpointRef: capability.EndpointBindingRef(envOr("OPENAI_ENDPOINT_REF", "openai-responses-default")), CredentialRef: capability.CredentialRef(envOr("OPENAI_CREDENTIAL_REF", "openai-advisory-v1")), Provider: openAIProvider})
|
||||
}
|
||||
gateway := cognitiveGateway{resolver: resolver, providers: providers, now: time.Now}
|
||||
gateway := cognitiveGateway{resolver: resolver, providers: providers, now: time.Now, allowCanary: os.Getenv("COGNITIVE_ALLOW_CANARY") == "true", canaryMaxOutputBytes: 4096}
|
||||
mux.Handle("/healthz", health.Handler{Runtime: "cognitive-worker", SemanticGeneration: 1, ArchitectureGeneration: 1})
|
||||
mux.HandleFunc("POST /v1/proposals/deterministic", deterministicProposal)
|
||||
mux.HandleFunc("POST /v1/advisory-policy", gateway.policyHandler)
|
||||
|
||||
@@ -118,6 +118,7 @@ services:
|
||||
LOCAL_CPU_PROVIDER_URL: http://docker.cin.su:18082/v1
|
||||
LOCAL_GPU_PROVIDER_URL: http://192.168.220.91:11434
|
||||
COGNITIVE_OWNER_PRINCIPAL_ID: domovoy-creator-principal
|
||||
COGNITIVE_ALLOW_CANARY: "true"
|
||||
depends_on: [core-runtime]
|
||||
|
||||
creator-web:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"task_file": "dev/tasks/DEV-000058-local-cognitive-canary.json",
|
||||
"task_id": "DEV-000058",
|
||||
"task_file": "dev/tasks/DEV-000059-local-cognitive-canary-observation.json",
|
||||
"task_id": "DEV-000059",
|
||||
"task_revision": 1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"task_id":"DEV-000059","task_revision":1,"architecture_revision":"ADR-0008","base_repo_revision":"2a0aee007352e8427619b458ab907ac945f19a64","risk_tier":"E",
|
||||
"intent":"Expose the exact validated local CPU canary to authenticated Creator advisory traffic behind a fail-closed server-side flag, collect typed usage/reliability observations, and retain immediate disable semantics without promoting the binding to ACTIVE.",
|
||||
"requirement_ids":["INV-OWN-001","INV-EPI-001","ADR-0006","ADR-0008","LOCAL-COGNITIVE-CANARY-OBSERVATION"],
|
||||
"allowed_change_scope":{"modules":["capability"],"paths":["cmd/cognitive-worker/**","deploy/**","tests/cognitive_canary/**","docs/evidence/**","docs/PROJECT_STATUS.md","dev/ACTIVE_TASK.json","dev/tasks/DEV-000059-local-cognitive-canary-observation.json"]},
|
||||
"forbidden_change_scope":{"paths":["architecture/**","contracts/**","modules/**","adapters/**","database/**","cmd/core-runtime/**","cmd/creator-web/**","security/**","ci/**",".gitea/**","cmd/devctl/**"]},
|
||||
"allowed_architecture_delta":{"new_cross_module_dependencies":0,"new_canonical_owners":0,"new_privileges":0,"new_third_party_dependencies":0,"new_provider_adapters":0},
|
||||
"contracts":{"consume":["creator-advisory-v1","CognitiveRequirement.AllowCanary","CognitiveUsageObservation"],"may_change":[]},
|
||||
"semantic_owner":"capability","canonical_state":["existing CognitiveUsageObservation only"],"state_transitions":["none; exact binding remains CANARY"],
|
||||
"side_effects_and_privileges":["bounded local CPU advisory inference for authenticated Creator","no OpenAI","no tools","no external mutation"],
|
||||
"persistence_and_migrations":["existing usage observation table only","immutable observation artifact","no migration","no event"],
|
||||
"failure_retry_restart":["flag defaults false","disable by removing flag and recreating cognitive worker","no automatic retry beyond existing single bounded fallback","provider loss returns typed failure"],
|
||||
"assumptions":["Creator continuation authorizes bounded canary exposure","docker-test remains prohibited"],
|
||||
"acceptance_scenarios":["flag absent excludes canary","flag true permits exact canary","usage observations bind exact revision","all requests remain advisory-only","binding remains CANARY","OpenAI usage remains zero"],
|
||||
"required_tests":["gateway flag unit tests","production authenticated smoke","usage DB evidence","disable path","go test ./...","go vet ./...","architecture sentinel","exact diff scope"],
|
||||
"terminal_gap_states":["BLOCKED_BY_ARCHITECTURE_GAP","BLOCKED_BY_SPEC_GAP","BLOCKED_BY_MISSING_CAPABILITY","BLOCKED_BY_CONFLICT"]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
# Assistant project status
|
||||
|
||||
Audit date: 2026-08-17. Active implementation: DEV-000058 revision 1.
|
||||
Audit date: 2026-08-17. Active implementation: DEV-000059 revision 1.
|
||||
|
||||
## Current milestone
|
||||
|
||||
@@ -12,6 +12,7 @@ Audit date: 2026-08-17. Active implementation: DEV-000058 revision 1.
|
||||
- Discovery never activates a provider, model or binding. Until a later benchmark and explicit promotion task, all discovered bindings remain `INACTIVE` and production remains fully LLM-free.
|
||||
- DEV-000057 executed a bounded synthetic qualification corpus against all 11 current local model revisions. Four exact binding revisions demonstrated the minimum advisory threshold; seven remain unsupported. Qualification evidence does not change lifecycle or activation.
|
||||
- DEV-000058 promoted the exact CPU `Qwen2.5-3B-Instruct-Q4_K_M` lineage to `VALIDATED/CANARY` through an authenticated, evidence-gated, append-only CAS transition. Ordinary routing still excludes it; only an explicit `AllowCanary` request can select it. All other current bindings remain `INACTIVE`.
|
||||
- DEV-000059 enables that exact canary behind a fail-closed cognitive-worker flag and a 4 KiB output clamp. Five authenticated synthetic Creator requests completed through the full production path with typed usage observations, zero failures and zero API cost. The binding remains `CANARY`, not `ACTIVE`.
|
||||
|
||||
## Delivered
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"artifact_id":"local-cpu-canary-observation-2026-08-17-v1",
|
||||
"binding_id":"binding-9e5e9779cd10b907c3695374",
|
||||
"binding_revision":2865230063392044815,
|
||||
"provider_model_id":"Qwen2.5-3B-Instruct-Q4_K_M",
|
||||
"path":"authenticated Creator Web to Cognitive Gateway to local CPU adapter to Capability usage owner",
|
||||
"synthetic_requests":5,
|
||||
"completed":5,
|
||||
"failed":0,
|
||||
"latency_millis":{"minimum":2347,"maximum":4603,"average":3646},
|
||||
"usage":{"input_units":225,"output_units":271,"actual_api_cost_micros":0},
|
||||
"canary_max_output_bytes":4096,
|
||||
"automatic_retry":false,
|
||||
"openai_requests":0,
|
||||
"activation":"CANARY"
|
||||
}
|
||||
Reference in New Issue
Block a user