승인된 execution preset을 Edge 조정 경계와 Node workspace/tool 실행 경계로 연결해 단일 요청 수명주기와 관측 계약을 일관되게 처리한다.
232 lines
8.6 KiB
Go
232 lines
8.6 KiB
Go
package openai
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"iop/apps/edge/internal/authprojection"
|
|
edgeservice "iop/apps/edge/internal/service"
|
|
"iop/packages/go/config"
|
|
)
|
|
|
|
var (
|
|
errSingleRequestBindingMissingStage = errors.New("single-request binding: missing stage")
|
|
errSingleRequestBindingDuplicate = errors.New("single-request binding: duplicate stage role")
|
|
errSingleRequestBindingUnauthorized = errors.New("single-request binding: stage model not authorized for principal")
|
|
errSingleRequestBindingDynamic = errors.New("single-request binding: stage model dynamically selected")
|
|
errSingleRequestBindingInconsistent = errors.New("single-request binding: option-inconsistent stage")
|
|
)
|
|
|
|
// compileSingleRequestBinding builds the surface-neutral immutable admission
|
|
// value from an authorized execution preset and its resolved canonical
|
|
// bindings. It is called only after the preset's selector and every referenced
|
|
// stage model have been verified through their canonical catalog bindings for
|
|
// the authenticated principal.
|
|
//
|
|
// The function rejects missing, duplicate, unauthorized, dynamically selected,
|
|
// or option-inconsistent inputs without generic fallback. It keeps the
|
|
// external model echo equal to the requested public model.
|
|
func compileSingleRequestBinding(
|
|
publicModel string,
|
|
preset config.ExecutionPreset,
|
|
bindings map[string]routeDispatch,
|
|
view authprojection.AuthenticatedView,
|
|
) (*edgeservice.SingleRequestBinding, error) {
|
|
if preset.SingleRequest == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
sr := preset.SingleRequest
|
|
|
|
// Defense-in-depth: independently re-verify the approved fixed shape at the
|
|
// admission boundary instead of trusting only the load-time config
|
|
// validation. A refreshed or crafted preset that no longer matches the frozen
|
|
// plan→work→review light shape must not compile an immutable admission.
|
|
if err := validateFixedSingleRequestShape(preset, sr); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Build the stage bindings from the preset's approved plan/work/review stage
|
|
// map, resolved through the canonical bindings authorized for the principal.
|
|
// Each stage's approved options come from the frozen policy config, never
|
|
// from dynamic provider dispatch metadata.
|
|
planBinding, err := resolveStageBinding("plan", sr.Stages.Plan, bindings, view)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("single-request plan stage: %w", err)
|
|
}
|
|
workBinding, err := resolveStageBinding("work", sr.Stages.Work, bindings, view)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("single-request work stage: %w", err)
|
|
}
|
|
reviewBinding, err := resolveStageBinding("review", sr.Stages.Review, bindings, view)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("single-request review stage: %w", err)
|
|
}
|
|
|
|
srLimits := sr.Limits
|
|
limits := edgeservice.SingleRequestLimits{
|
|
WallClockMS: srLimits.WallClockMS,
|
|
StageTimeoutMS: srLimits.StageTimeoutMS,
|
|
MaxToolIterations: srLimits.MaxToolIterations,
|
|
MaxOutputBytes: srLimits.MaxOutputBytes,
|
|
}
|
|
|
|
return edgeservice.NewSingleRequestBinding(
|
|
publicModel,
|
|
sr.WorkspaceRef,
|
|
*planBinding,
|
|
*workBinding,
|
|
*reviewBinding,
|
|
limits,
|
|
)
|
|
}
|
|
|
|
// validateFixedSingleRequestShape re-verifies the approved immutable
|
|
// single-request shape at admission time. It independently confirms the
|
|
// selector, allowed modes, and the single light route match the frozen
|
|
// plan→work→review policy stages, including high reasoning on plan/review and
|
|
// no reasoning option on work. Every violation maps to a typed single-request
|
|
// binding error without generic fallback.
|
|
func validateFixedSingleRequestShape(preset config.ExecutionPreset, sr *config.ExecutionSingleRequestPolicy) error {
|
|
// Allowed modes must be exactly ["light"].
|
|
if len(preset.AllowedModes) != 1 || preset.AllowedModes[0] != config.ModeLight {
|
|
return errSingleRequestBindingInconsistent
|
|
}
|
|
|
|
// The fused selector must exactly match the fixed plan stage (model and
|
|
// options); the selector cannot diverge from the frozen plan binding.
|
|
if preset.Selector.Model != sr.Stages.Plan.Model || !singleRequestOptionsEqual(preset.Selector.Options, sr.Stages.Plan.Options) {
|
|
return errSingleRequestBindingInconsistent
|
|
}
|
|
|
|
// Plan and review must declare high reasoning; work must not declare it.
|
|
if singleRequestReasoningEffort(sr.Stages.Plan.Options) != config.SingleRequestReasoningEffortHigh {
|
|
return errSingleRequestBindingInconsistent
|
|
}
|
|
if singleRequestReasoningEffort(sr.Stages.Review.Options) != config.SingleRequestReasoningEffortHigh {
|
|
return errSingleRequestBindingInconsistent
|
|
}
|
|
if _, present := sr.Stages.Work.Options["reasoning_effort"]; present {
|
|
return errSingleRequestBindingInconsistent
|
|
}
|
|
|
|
// Exactly one light route with ordered, unique plan→work→review roles whose
|
|
// model and options exactly match the frozen policy stages.
|
|
if len(preset.Routes) != 1 {
|
|
return errSingleRequestBindingInconsistent
|
|
}
|
|
route, ok := preset.Routes[config.ModeLight]
|
|
if !ok {
|
|
return errSingleRequestBindingMissingStage
|
|
}
|
|
expected := []struct {
|
|
role string
|
|
stage config.ExecutionSingleRequestStageConfig
|
|
}{
|
|
{"plan", sr.Stages.Plan},
|
|
{"work", sr.Stages.Work},
|
|
{"review", sr.Stages.Review},
|
|
}
|
|
if len(route.Stages) != len(expected) {
|
|
return errSingleRequestBindingMissingStage
|
|
}
|
|
seenRoles := make(map[string]struct{}, len(route.Stages))
|
|
for _, stage := range route.Stages {
|
|
if _, dup := seenRoles[stage.Role]; dup {
|
|
return errSingleRequestBindingDuplicate
|
|
}
|
|
seenRoles[stage.Role] = struct{}{}
|
|
}
|
|
for i, want := range expected {
|
|
st := route.Stages[i]
|
|
if st.Role != want.role {
|
|
return errSingleRequestBindingInconsistent
|
|
}
|
|
if st.Model != want.stage.Model {
|
|
// The route would dynamically select a downstream model other than
|
|
// the frozen policy stage model.
|
|
return errSingleRequestBindingDynamic
|
|
}
|
|
if !singleRequestOptionsEqual(st.Options, want.stage.Options) {
|
|
return errSingleRequestBindingInconsistent
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// resolveStageBinding maps a frozen stage config to its authorized routeDispatch
|
|
// binding and copies the approved stage options into a service DTO. It verifies
|
|
// that the binding is present, managed, principal-consistent, and names exactly
|
|
// the canonical model the frozen stage declares.
|
|
func resolveStageBinding(role string, stage config.ExecutionSingleRequestStageConfig, bindings map[string]routeDispatch, view authprojection.AuthenticatedView) (*edgeservice.SingleRequestStageBinding, error) {
|
|
canonicalModel := stage.Model
|
|
dispatch, ok := bindings[canonicalModel]
|
|
if !ok {
|
|
return nil, errSingleRequestBindingMissingStage
|
|
}
|
|
|
|
// The binding must come from a managed principal resolution. Unmanaged
|
|
// legacy routes cannot back a single-request admission.
|
|
if !dispatch.Managed {
|
|
return nil, errSingleRequestBindingUnauthorized
|
|
}
|
|
|
|
// Verify the binding's model group matches the canonical reference.
|
|
if dispatch.ModelGroupKey != canonicalModel {
|
|
return nil, errSingleRequestBindingInconsistent
|
|
}
|
|
|
|
// Verify the binding's principal matches the authenticated view.
|
|
if dispatch.PrincipalRef != view.Principal.PrincipalRef {
|
|
return nil, errSingleRequestBindingUnauthorized
|
|
}
|
|
|
|
// Copy the approved stage-level options from the frozen policy stage config,
|
|
// not from dynamic provider dispatch metadata. NewSingleRequestBinding takes
|
|
// a defensive deep copy, so a later config refresh cannot mutate an admitted
|
|
// binding through this reference.
|
|
return &edgeservice.SingleRequestStageBinding{
|
|
Model: canonicalModel,
|
|
Options: stage.Options,
|
|
}, nil
|
|
}
|
|
|
|
// compileSingleRequestBindingForUnmanaged builds the service binding from an
|
|
// unmanaged (legacy) preset resolution. It rejects the compilation because
|
|
// single-request admission requires managed principal authorization.
|
|
func compileSingleRequestBindingForUnmanaged(publicModel string, preset config.ExecutionPreset) (*edgeservice.SingleRequestBinding, error) {
|
|
if preset.SingleRequest == nil {
|
|
return nil, nil
|
|
}
|
|
// Unmanaged presets cannot back a single-request admission because there
|
|
// is no authenticated principal to verify stage authorization against.
|
|
return nil, errSingleRequestBindingUnauthorized
|
|
}
|
|
|
|
// singleRequestOptionsEqual reports whether two option maps are equal, treating
|
|
// nil and empty maps as equal.
|
|
func singleRequestOptionsEqual(a, b map[string]any) bool {
|
|
if len(a) == 0 && len(b) == 0 {
|
|
return true
|
|
}
|
|
return reflect.DeepEqual(a, b)
|
|
}
|
|
|
|
// singleRequestReasoningEffort extracts the reasoning_effort option value from a
|
|
// stage's options map, returning "" when absent or non-string.
|
|
func singleRequestReasoningEffort(opts map[string]any) string {
|
|
if opts == nil {
|
|
return ""
|
|
}
|
|
v, ok := opts["reasoning_effort"]
|
|
if !ok {
|
|
return ""
|
|
}
|
|
s, ok := v.(string)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
return s
|
|
}
|