Files
query-orchestration/internal/serviceconfig/chatbot/config.go
T

83 lines
3.7 KiB
Go
Raw Normal View History

// Package chatbot holds the FastAPI agent service client configuration.
//
// The struct, env tags, and validation rules are sourced directly from
// plans/chatbot_plan_codex.v10.md §8 (Configuration). Required string
// fields use the standard `required,notEmpty` env tag pair so the
// caarlos0/env loader rejects empty values before Validate runs; the
// numeric fields default to plan-mandated values and are checked by
// Validate.
package chatbot
import "fmt"
// StateMaxBytes is the per-session state cap enforced when persisting
// updated_session_state from a FastAPI response. Plan §8 derived value.
// 64 KiB matches the plan literal `64 * 1024`.
const StateMaxBytes = 64 * 1024
// MaxTurnCharsCeiling is the largest legal value for MaxTurnChars.
// It mirrors the OpenAPI schema constraint on the prompt body at
// serviceAPIs/queryAPI.yaml (BotTurnAddRequest.prompt.maxLength=65536).
// Setting CHATBOT_MAX_TURN_CHARS above this ceiling would create a
// silent inconsistency: the schema validator would reject oversized
// prompts before the service-layer length guard ran, leaving operators
// with no signal that the configured cap is unreachable. Any change to
// this value must be made in lockstep with the OpenAPI spec.
const MaxTurnCharsCeiling = 65536
// ChatbotConfig is the queryAPI-side configuration block for the FastAPI
// chatbot agent service. Fields, env-var names, defaults, and the
// notEmpty/required tags are dictated verbatim by plan §8.
type ChatbotConfig struct {
// ServiceURL is the base URL of the FastAPI agent service. No
// envDefault: production must not have a silent default.
ServiceURL string `env:"CHATBOT_SERVICE_URL,required,notEmpty"`
// APIKey is the bearer credential the backend presents on each call.
// No envDefault for the same reason as ServiceURL.
APIKey string `env:"CHATBOT_API_KEY,required,notEmpty"`
// RequestTimeoutSeconds is the per-call wall-clock budget for the
// FastAPI client. Default 60s per plan §8.
RequestTimeoutSeconds int `env:"CHATBOT_REQUEST_TIMEOUT_SECONDS" envDefault:"60"`
// MaxTurnChars caps prompt and completion length in runes. Default
// 2000 per plan §8.
MaxTurnChars int `env:"CHATBOT_MAX_TURN_CHARS" envDefault:"2000"`
// RecentTurns is the conversation-history length sent to FastAPI on
// each request and the upper bound on session-list preview turns.
// Plan §8 mandates the range [1, 5]; default is 5.
RecentTurns int `env:"CHATBOT_RECENT_TURNS" envDefault:"5"`
}
// Validate enforces the numeric range constraints from plan §8.
// ServiceURL/APIKey emptiness is enforced by the env loader's notEmpty
// tag; duplicating it here would mask loader-time rejection. The error
// message names the offending field so test-eng's table assertions match
// on substring.
func (c ChatbotConfig) Validate() error {
if c.RequestTimeoutSeconds < 1 {
return fmt.Errorf("chatbot config: RequestTimeoutSeconds must be >= 1, got %d", c.RequestTimeoutSeconds)
}
if c.MaxTurnChars < 1 {
return fmt.Errorf("chatbot config: MaxTurnChars must be >= 1, got %d", c.MaxTurnChars)
}
if c.MaxTurnChars > MaxTurnCharsCeiling {
return fmt.Errorf(
"chatbot config: MaxTurnChars must be <= %d (OpenAPI prompt.maxLength), got %d",
MaxTurnCharsCeiling, c.MaxTurnChars)
}
if c.RecentTurns < 1 || c.RecentTurns > 5 {
return fmt.Errorf("chatbot config: RecentTurns must be in [1, 5], got %d", c.RecentTurns)
}
return nil
}
// InflightGraceSeconds is the grace window used by AddTurn to abandon a
// stuck in_flight turn. Plan §8 sets it to twice the request timeout so a
// single retry cycle that exhausts the timeout is not abandoned mid-flight.
func (c ChatbotConfig) InflightGraceSeconds() int {
return 2 * c.RequestTimeoutSeconds
}