This commit is contained in:
@@ -157,7 +157,10 @@ func main() {
|
||||
mux.Handle("/readyz", health.ReadinessHandler{Runtime: "core-runtime", Probes: []health.Probe{
|
||||
health.TCPProbe("postgres", envOr("POSTGRES_HOST", "postgres"), envOr("POSTGRES_PORT", "5432")),
|
||||
health.HTTPProbe(readinessClient, strings.TrimRight(envOr("CONTINUITY_RUNTIME_URL", "http://continuity-runtime:8081"), "/")+"/healthz"),
|
||||
health.HTTPProbe(readinessClient, strings.TrimRight(envOr("COGNITIVE_RUNTIME_URL", "http://cognitive-worker:8083"), "/")+"/healthz"),
|
||||
// A running worker is not necessarily ready to serve cognitive traffic.
|
||||
// Keep deterministic paths alive, but do not advertise overall readiness
|
||||
// while the cognitive dependency is still warming.
|
||||
health.HTTPProbe(readinessClient, strings.TrimRight(envOr("COGNITIVE_RUNTIME_URL", "http://cognitive-worker:8083"), "/")+"/readyz"),
|
||||
}})
|
||||
admissionPolicy := capability.AdmissionPolicy{ID: envOr("COGNITIVE_ADMISSION_POLICY_ID", "creator-advisory-local-v1"), MaxConcurrent: envInt("COGNITIVE_ADMISSION_MAX_CONCURRENT", 1), MaxAcceptedPerHour: envInt("COGNITIVE_ADMISSION_MAX_ACCEPTED_PER_HOUR", 60), MaxInputBytes: envInt("COGNITIVE_ADMISSION_MAX_INPUT_BYTES", 80<<10), MaxOutputBytes: envInt("COGNITIVE_ADMISSION_MAX_OUTPUT_BYTES", 16<<10)}
|
||||
mux.HandleFunc("POST /v1/internal/cognitive/admission", func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -806,6 +809,12 @@ func main() {
|
||||
ReceivedAtUnixMillis: utterance.ReceivedAtUnixMillis,
|
||||
}
|
||||
helpPath := workdomain.DetermineHelpPath(planningInput)
|
||||
// Introspection requests are read-only household UX. A cognitive
|
||||
// candidate must not reinterpret them as an executable delegation.
|
||||
switch semantic.InformationRequestKind {
|
||||
case interaction.InformationRequestCapabilityInventory, interaction.InformationRequestLocalCapabilityInventory, interaction.InformationRequestCognitiveModelInventory, interaction.InformationRequestCognitiveCurrentModel, interaction.InformationRequestCognitiveChannelStatus, interaction.InformationRequestCognitiveRecovery, interaction.InformationRequestMemoryInventory:
|
||||
helpPath = work.HelpPath{}
|
||||
}
|
||||
if responseErr == nil && responseResult.Handled {
|
||||
helpPath = work.HelpPath{}
|
||||
}
|
||||
|
||||
@@ -258,10 +258,17 @@ services:
|
||||
LOCAL_IMAGE_MODEL: ${LOCAL_IMAGE_MODEL:-z_image_turbo_bf16.safetensors}
|
||||
COGNITIVE_OWNER_PRINCIPAL_ID: domovoy-creator-principal
|
||||
COGNITIVE_ALLOW_CANARY: "true"
|
||||
depends_on: [core-runtime, cognitive-lifeboat]
|
||||
volumes:
|
||||
- assistant_core_secrets:/run/secrets:ro
|
||||
networks: [default, llm-local]
|
||||
depends_on:
|
||||
core-runtime: { condition: service_started }
|
||||
cognitive-lifeboat: { condition: service_healthy }
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:8083/healthz >/dev/null && wget -qO- http://localhost:8083/readyz >/dev/null"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
|
||||
cognitive-lifeboat:
|
||||
image: ghcr.io/ggml-org/llama.cpp:server
|
||||
@@ -328,7 +335,11 @@ services:
|
||||
COGNITIVE_RUNTIME_URL: http://cognitive-worker:8083
|
||||
AGENT_WORKER_URL: http://agent-worker:8090
|
||||
ASSISTANT_BUILD_REVISION: ${ASSISTANT_SOURCE_REVISION:-unknown}
|
||||
depends_on: [core-runtime, continuity-runtime, commit-runtime, cognitive-worker]
|
||||
depends_on:
|
||||
core-runtime: { condition: service_started }
|
||||
continuity-runtime: { condition: service_started }
|
||||
commit-runtime: { condition: service_started }
|
||||
cognitive-worker: { condition: service_healthy }
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:8084/healthz >/dev/null"]
|
||||
interval: 10s
|
||||
|
||||
@@ -616,6 +616,9 @@ func sourceDependentQuestion(primary, secondary interaction.SpeechAct, tokens []
|
||||
if !inquiry {
|
||||
return false
|
||||
}
|
||||
if focusIntrospectionQuestion(tokens) {
|
||||
return false
|
||||
}
|
||||
currentCue := containsStemInTokens(tokens, "сейчас", "сегодня", "текущ", "актуаль", "последн", "прямо")
|
||||
observedStateCue := containsStemInTokens(tokens, "погод", "температур", "осад", "ветер", "курс", "цен", "стоим", "новост", "распис", "событ", "статус", "доступен", "работает", "запущен", "срок") || approximateToken(tokens, "погод", 2)
|
||||
placeSearchCue := containsStemInTokens(tokens, "ресторан", "кафе", "отел", "магаз", "мест", "ближ", "адрес", "организац", "бренд", "товар", "артикул", "продавец", "маркетплейс", "wildberries", "wb")
|
||||
@@ -710,7 +713,7 @@ func informationRequestKind(message string, candidate interaction.SemanticCandid
|
||||
if cognitiveSubject && inventoryRequest && inventoryCue {
|
||||
return interaction.InformationRequestCognitiveModelInventory
|
||||
}
|
||||
capabilitySubject := containsStemInTokens(tokens, "уме", "возможност", "способн", "capabilit") || (containsMeaningfulInterrogative(tokens) && containsToken(tokens, "можешь"))
|
||||
capabilitySubject := containsStemInTokens(tokens, "уме", "возможност", "способн", "доступн", "инструмент", "capabilit") || (containsMeaningfulInterrogative(tokens) && containsToken(tokens, "можешь"))
|
||||
if inquiry && capabilitySubject {
|
||||
if containsStemInTokens(tokens, "без", "внешн", "локальн") && containsStemInTokens(tokens, "модел", "ии", "ai") {
|
||||
return interaction.InformationRequestLocalCapabilityInventory
|
||||
@@ -727,6 +730,13 @@ func informationRequestKind(message string, candidate interaction.SemanticCandid
|
||||
return ""
|
||||
}
|
||||
|
||||
func focusIntrospectionQuestion(tokens []string) bool {
|
||||
if containsStemInTokens(tokens, "фокус", "focus", "контекст") && (containsStemInTokens(tokens, "сейчас", "текущ", "работ", "контекст") || containsMeaningfulInterrogative(tokens)) {
|
||||
return true
|
||||
}
|
||||
return containsStemInTokens(tokens, "над", "делаем", "работаем") && containsStemInTokens(tokens, "сейчас", "текущ", "мы")
|
||||
}
|
||||
|
||||
func LocationContextReply(message string) string {
|
||||
if location := personalCurrentLocation(message); location != "" {
|
||||
return "Запомнил: сейчас вы в " + location + "."
|
||||
|
||||
@@ -142,6 +142,7 @@ func TestInformationRequestsAreClassifiedOnceByInteraction(t *testing.T) {
|
||||
{"почему ты не смог найти ответ?", interaction.InformationRequestResearchFailureExplanation},
|
||||
{"что ты можешь сделать без внешних моделей?", interaction.InformationRequestLocalCapabilityInventory},
|
||||
{"что ты умеешь?", interaction.InformationRequestCapabilityInventory},
|
||||
{"что доступно в этом чате?", interaction.InformationRequestCapabilityInventory},
|
||||
{"проверь состояние локального канала", interaction.InformationRequestCognitiveChannelStatus},
|
||||
{"нет связи с ИИ, восстанови канал", interaction.InformationRequestCognitiveRecovery},
|
||||
{"сообщии список моделей", interaction.InformationRequestCognitiveModelInventory},
|
||||
|
||||
@@ -18,6 +18,7 @@ type PersonalMemory struct {
|
||||
|
||||
type PersonalMemoryFact struct {
|
||||
Kind string
|
||||
Subject string
|
||||
Value string
|
||||
ConfidenceMillis int64
|
||||
SourceUtteranceID interaction.HumanUtteranceID
|
||||
@@ -54,6 +55,7 @@ func CompilePersonalMemory(history []interaction.HumanUtterance) PersonalMemory
|
||||
if preference := personalPreference(utterance.Content); preference != "" {
|
||||
preferences = append(preferences, PersonalMemoryFact{
|
||||
Kind: "PREFERENCE",
|
||||
Subject: preferenceSubject(preference),
|
||||
Value: preference,
|
||||
ConfidenceMillis: 900,
|
||||
SourceUtteranceID: utterance.ID,
|
||||
@@ -75,6 +77,21 @@ func CompilePersonalMemory(history []interaction.HumanUtterance) PersonalMemory
|
||||
return result
|
||||
}
|
||||
|
||||
// LatestPreference returns the current value for a semantic subject while
|
||||
// retaining older facts for historical evidence.
|
||||
func (m PersonalMemory) LatestPreference(subject string) (PersonalMemoryFact, bool) {
|
||||
var latest PersonalMemoryFact
|
||||
for _, fact := range m.Facts {
|
||||
if fact.Kind != "PREFERENCE" || fact.Subject != subject {
|
||||
continue
|
||||
}
|
||||
if latest.Kind == "" || fact.ObservedAtUnixMillis >= latest.ObservedAtUnixMillis {
|
||||
latest = fact
|
||||
}
|
||||
}
|
||||
return latest, latest.Kind != ""
|
||||
}
|
||||
|
||||
func (m PersonalMemory) CurrentLocation() string {
|
||||
for _, fact := range m.Facts {
|
||||
if fact.Kind == personalMemoryCurrentLocation {
|
||||
@@ -112,11 +129,16 @@ func RecallPersonalPreference(history []interaction.HumanUtterance, question str
|
||||
if !strings.Contains(lowerQuestion, "цвет") || (!strings.Contains(lowerQuestion, "какой") && !strings.Contains(lowerQuestion, "назвал")) {
|
||||
return ""
|
||||
}
|
||||
for _, fact := range CompilePersonalMemory(history).Facts {
|
||||
if fact.Kind != "PREFERENCE" || !strings.Contains(strings.ToLower(fact.Value), "цвет") {
|
||||
memory := CompilePersonalMemory(history)
|
||||
for _, fact := range memory.Facts {
|
||||
if fact.Kind != "PREFERENCE" || fact.Subject != "тестовый цвет" {
|
||||
continue
|
||||
}
|
||||
value := strings.TrimSpace(fact.Value)
|
||||
current, ok := memory.LatestPreference(fact.Subject)
|
||||
if !ok || current.SourceUtteranceID != fact.SourceUtteranceID {
|
||||
continue
|
||||
}
|
||||
value := strings.TrimSpace(current.Value)
|
||||
if separator := strings.Index(value, "—"); separator >= 0 {
|
||||
value = strings.TrimSpace(value[separator+len("—"):])
|
||||
}
|
||||
@@ -127,6 +149,13 @@ func RecallPersonalPreference(history []interaction.HumanUtterance, question str
|
||||
return ""
|
||||
}
|
||||
|
||||
func preferenceSubject(preference string) string {
|
||||
if separator := strings.Index(preference, "—"); separator >= 0 {
|
||||
return strings.TrimSpace(preference[:separator])
|
||||
}
|
||||
return strings.TrimSpace(preference)
|
||||
}
|
||||
|
||||
func personalCurrentLocation(message string) string {
|
||||
tokens := semanticTokens(message)
|
||||
if len(tokens) < 4 || tokens[0] != "я" || !containsStemInTokens(tokens, "сейчас", "нахож", "жив") {
|
||||
|
||||
@@ -48,3 +48,17 @@ func TestRecallPersonalPreferenceUsesProjectedAssertion(t *testing.T) {
|
||||
t.Fatalf("answer=%q", answer)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompilePersonalMemoryRanksCurrentSubjectWithoutDroppingHistory(t *testing.T) {
|
||||
memory := CompilePersonalMemory([]interaction.HumanUtterance{
|
||||
{ID: "old", Content: "запомни: мой тестовый цвет — фиолетовый", ReceivedAtUnixMillis: 100},
|
||||
{ID: "new", Content: "запомни: мой тестовый цвет — acceptance-m5-синий-914", ReceivedAtUnixMillis: 200},
|
||||
})
|
||||
current, ok := memory.LatestPreference("тестовый цвет")
|
||||
if !ok || current.Value != "тестовый цвет — acceptance-m5-синий-914" {
|
||||
t.Fatalf("current=%+v ok=%t", current, ok)
|
||||
}
|
||||
if len(memory.Facts) != 2 {
|
||||
t.Fatalf("historical evidence was dropped: %+v", memory.Facts)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,9 +86,12 @@ func InterpretUtterance(utterance interaction.HumanUtterance, projects []situati
|
||||
result.Relations = append(result.Relations, relations...)
|
||||
}
|
||||
result.Reply = semanticReply(utterance.Content, result, previousFocus)
|
||||
if isCurrentFocusQuestion(utterance.Content, selectedCandidate.PrimarySpeechAct) && previousFocus != nil {
|
||||
if isCurrentFocusQuestion(utterance.Content, selectedCandidate.PrimarySpeechAct) {
|
||||
result.NeedsCognition = false
|
||||
result.Interpretation.NeedsCognition = false
|
||||
result.CapabilityHints = nil
|
||||
selectedCandidate.CapabilityHints = nil
|
||||
result.Candidates[0] = selectedCandidate
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -343,8 +346,10 @@ func semanticReply(message string, result SemanticResult, previousFocus *situati
|
||||
case interaction.SpeechActRejection:
|
||||
return "Понял."
|
||||
case interaction.SpeechActQuestion:
|
||||
lower := strings.ToLower(message)
|
||||
if previousFocus != nil && (strings.Contains(lower, "сейчас") || strings.Contains(lower, "что мы") || strings.Contains(lower, "делаем")) {
|
||||
if focusIntrospectionQuestion(semanticTokens(message)) {
|
||||
if previousFocus == nil {
|
||||
return "Сейчас активный Focus не выбран."
|
||||
}
|
||||
return "Сейчас в фокусе: " + previousFocus.ProjectDisplayName + "."
|
||||
}
|
||||
if result.Interpretation.Applicability == interaction.SemanticNeedsBroaderInterpret || result.Interpretation.Applicability == interaction.SemanticLowConfidence {
|
||||
@@ -385,8 +390,7 @@ func isCurrentFocusQuestion(message string, act interaction.SpeechAct) bool {
|
||||
if act != interaction.SpeechActQuestion {
|
||||
return false
|
||||
}
|
||||
lower := strings.ToLower(message)
|
||||
return strings.Contains(lower, "сейчас") || strings.Contains(lower, "что мы") || strings.Contains(lower, "делаем")
|
||||
return focusIntrospectionQuestion(semanticTokens(message))
|
||||
}
|
||||
|
||||
func hasCorrectionMeaning(candidate interaction.SemanticCandidate, utterance interaction.HumanUtterance) bool {
|
||||
|
||||
@@ -293,6 +293,13 @@ func TestInterpretUtteranceQuestionAboutCurrentWorkUsesFocus(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterpretUtteranceEnglishFocusIntrospectionStaysLocal(t *testing.T) {
|
||||
result := interpretForTest(t, "Какой сейчас Focus разговора?", activeFocus("assistant", "Assistant"), "")
|
||||
if result.Reply != "Сейчас в фокусе: Assistant." || result.NeedsCognition || len(result.CapabilityHints) != 0 {
|
||||
t.Fatalf("focus introspection escaped local route: %+v", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterpretUtteranceInheritedFocusForThere(t *testing.T) {
|
||||
result := interpretForTest(t, "Добавь туда новую настройку.", activeFocus("assistant", "Assistant"), "")
|
||||
if len(result.Projects) != 1 || result.Projects[0].ProjectID != "assistant" || result.Projects[0].Provenance != situation.ProjectResolutionInheritedFocus {
|
||||
|
||||
Reference in New Issue
Block a user