package main import ( "encoding/json" "fmt" "net/http" "strings" "git.cin.su/m/Assistant/contracts/capability" "git.cin.su/m/Assistant/contracts/cognitive" ) func (g cognitiveGateway) interpretationHandler(w http.ResponseWriter, r *http.Request) { var request cognitive.GatewayInterpretationRequest if !decodeJSON(w, r, 80<<10, &request) { return } if request.Version != cognitive.CreatorAdvisoryV1 || request.RequestID == "" || request.ActorID == "" || request.PrincipalID == "" || request.ScopeID == "" || strings.TrimSpace(request.Message) == "" || request.MaxOutputBytes < 1 { http.Error(w, "invalid interpretation request", http.StatusBadRequest) return } 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 { writeInterpretationResponse(w, cognitive.GatewayInterpretationResponse{Version: request.Version, RequestID: request.RequestID, FinishReason: cognitive.FinishProviderUnavailable}) return } provider := g.provider(selected) if provider == nil { writeInterpretationResponse(w, cognitive.GatewayInterpretationResponse{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: interpretationPrompt(request), 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 { response = cognitive.AdvisoryResponse{Version: request.Version, RequestID: request.RequestID, ProviderBindingID: selected.Binding.ID, ProviderBindingRevision: selected.Binding.Revision, FinishReason: cognitive.FinishInvalidProviderResponse} } g.observe(r.Context(), selected, response, started) if response.FinishReason != cognitive.FinishCompleted { writeInterpretationResponse(w, cognitive.GatewayInterpretationResponse{Version: request.Version, RequestID: request.RequestID, FinishReason: response.FinishReason, Usage: response.Usage}) return } candidate, ok := parseInterpretationCandidate(response.AdvisoryText) if !ok { writeInterpretationResponse(w, cognitive.GatewayInterpretationResponse{Version: request.Version, RequestID: request.RequestID, FinishReason: cognitive.FinishInvalidStructuredOutput, Usage: response.Usage}) return } writeInterpretationResponse(w, cognitive.GatewayInterpretationResponse{Version: request.Version, RequestID: request.RequestID, Candidate: candidate, FinishReason: cognitive.FinishCompleted, Usage: response.Usage}) } func interpretationPrompt(request cognitive.GatewayInterpretationRequest) string { projects := "none" if len(request.ProjectNames) > 0 { projects = strings.Join(request.ProjectNames, ", ") } return fmt.Sprintf(`Return only strict JSON with keys primary_speech_act, secondary_speech_act, confidence_millis, applicability, requested_outcome, needs_cognition, clarification, selection_reason, capability_hints. Allowed speech acts: DIRECTIVE, ASSERTION, DECISION, PREFERENCE, QUESTION, HYPOTHESIS, CORRECTION, REQUEST_TO_INVESTIGATE, CONFIRMATION, REJECTION, CANCELLATION, CONVERSATIONAL, UNKNOWN. Allowed applicability: APPLICABLE, LOW_CONFIDENCE, MIXED_INTENT, NEEDS_BROADER_INTERPRETATION, CLARIFICATION_REQUIRED. Allowed clarification: NOT_REQUIRED or REQUIRED. Capability hints may only contain known capability kinds like COGNITIVE_REASONING, MODIFY_CODE, RUN_TESTS, SEND_EMAIL, SCHEDULE_REMINDER, VERIFY_FACT, INSPECT_PROJECT, READ_DATABASE, QUERY_LOGS. Interpret the human utterance semantically. Do not claim action execution. Do not treat punctuation alone as decisive. If uncertain, set applicability accordingly. Message: %q Deterministic primary: %s Deterministic secondary: %s Deterministic confidence: %d Deterministic applicability: %s Deterministic requested outcome: %q Relevant project names: %s`, request.Message, request.DeterministicSpeechAct, request.DeterministicSecondary, request.DeterministicConfidence, request.DeterministicApplicability, request.RequestedOutcome, projects) } func parseInterpretationCandidate(text string) (cognitive.GatewayInterpretationCandidate, bool) { start := strings.Index(text, "{") end := strings.LastIndex(text, "}") if start < 0 || end <= start { return cognitive.GatewayInterpretationCandidate{}, false } var candidate cognitive.GatewayInterpretationCandidate if err := json.Unmarshal([]byte(text[start:end+1]), &candidate); err != nil { return cognitive.GatewayInterpretationCandidate{}, false } if !validSpeechAct(candidate.PrimarySpeechAct) || candidate.ConfidenceMillis < 0 || candidate.ConfidenceMillis > 1000 { return cognitive.GatewayInterpretationCandidate{}, false } if candidate.SecondarySpeechAct != "" && !validSpeechAct(candidate.SecondarySpeechAct) { return cognitive.GatewayInterpretationCandidate{}, false } switch candidate.Applicability { case cognitive.InterpretationApplicable, cognitive.InterpretationLowConfidence, cognitive.InterpretationMixedIntent, cognitive.InterpretationNeedsBroader, cognitive.InterpretationClarificationRequired: default: return cognitive.GatewayInterpretationCandidate{}, false } if candidate.Clarification != "NOT_REQUIRED" && candidate.Clarification != "REQUIRED" { return cognitive.GatewayInterpretationCandidate{}, false } return candidate, true } func validSpeechAct(value string) bool { switch value { case "DIRECTIVE", "ASSERTION", "DECISION", "PREFERENCE", "QUESTION", "HYPOTHESIS", "CORRECTION", "REQUEST_TO_INVESTIGATE", "CONFIRMATION", "REJECTION", "CANCELLATION", "CONVERSATIONAL", "UNKNOWN": return true } return false } func writeInterpretationResponse(w http.ResponseWriter, response cognitive.GatewayInterpretationResponse) { w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(response) }