nexo-brain 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -5
- package/package.json +1 -1
- package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
- package/src/__pycache__/cognitive.cpython-314.pyc +0 -0
- package/src/__pycache__/db.cpython-314.pyc +0 -0
- package/src/__pycache__/evolution_cycle.cpython-314.pyc +0 -0
- package/src/__pycache__/kg_populate.cpython-314.pyc +0 -0
- package/src/__pycache__/knowledge_graph.cpython-314.pyc +0 -0
- package/src/__pycache__/maintenance.cpython-314.pyc +0 -0
- package/src/__pycache__/migrate_embeddings.cpython-314.pyc +0 -0
- package/src/__pycache__/plugin_loader.cpython-314.pyc +0 -0
- package/src/__pycache__/server.cpython-314.pyc +0 -0
- package/src/__pycache__/storage_router.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_coordination.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_credentials.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_learnings.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_menu.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_reminders.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_reminders_crud.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_sessions.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_task_history.cpython-314.pyc +0 -0
- package/src/cognitive.py +63 -14
- package/src/dashboard/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/dashboard/__pycache__/app.cpython-314.pyc +0 -0
- package/src/dashboard/app.py +1 -1
- package/src/db.py +29 -13
- package/src/evolution_cycle.py +72 -94
- package/src/hooks/__pycache__/auto_capture.cpython-314.pyc +0 -0
- package/src/hooks/session-start.sh +5 -2
- package/src/hooks/session-stop.sh +79 -133
- package/src/knowledge_graph.py +3 -3
- package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/agents.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/artifact_registry.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/backup.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/cognitive_memory.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/core_rules.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/cortex.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/entities.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/episodic_memory.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/evolution.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/guard.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/knowledge_graph_tools.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/preferences.cpython-314.pyc +0 -0
- package/src/plugins/artifact_registry.py +450 -0
- package/src/plugins/cognitive_memory.py +9 -9
- package/src/plugins/episodic_memory.py +8 -8
- package/src/plugins/evolution.py +4 -4
- package/src/plugins/guard.py +26 -2
- package/src/rules/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/rules/__pycache__/migrate.cpython-314.pyc +0 -0
- package/src/rules/core-rules 2.json +329 -0
- package/src/scripts/__pycache__/check-context.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-auto-update.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-catchup.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-evolution-run.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-immune.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-validator.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-pre-commit.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-reflection.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-email.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-reply.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-sleep.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-synthesis.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-314.pyc +0 -0
- package/src/scripts/check-context.py +257 -0
- package/src/scripts/nexo-brain-activation.sh +140 -0
- package/src/scripts/nexo-catchup.py +59 -5
- package/src/scripts/nexo-daily-self-audit.py +168 -183
- package/src/scripts/nexo-evolution-run.py +23 -31
- package/src/scripts/nexo-followup-hygiene.py +107 -0
- package/src/scripts/nexo-immune.py +108 -91
- package/src/scripts/nexo-learning-validator.py +226 -0
- package/src/scripts/nexo-postmortem-consolidator.py +230 -414
- package/src/scripts/nexo-pre-commit.py +118 -0
- package/src/scripts/nexo-proactive-dashboard.py +342 -0
- package/src/scripts/nexo-runtime-preflight.py +270 -0
- package/src/scripts/nexo-send-email.py +25 -0
- package/src/scripts/nexo-send-reply.py +176 -0
- package/src/scripts/nexo-sleep.py +283 -527
- package/src/scripts/nexo-snapshot-restore.sh +34 -0
- package/src/scripts/nexo-synthesis.py +141 -432
- package/src/scripts/nexo-watchdog-smoke.py +114 -0
- package/src/server.py +5 -5
- package/src/tools_coordination.py +3 -3
- package/src/tools_learnings.py +1 -1
- package/src/tools_menu.py +31 -49
- package/src/tools_reminders_crud.py +1 -1
- package/src/tools_sessions.py +12 -12
|
@@ -1,27 +1,22 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# NEXO Stop
|
|
2
|
+
# NEXO Memory Stop Hook (v7 — BLOCKING post-mortem with trivial session detection)
|
|
3
3
|
#
|
|
4
4
|
# v5 bug: used "approve" + systemMessage — AI never processed post-mortem.
|
|
5
|
-
# v6
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# Non-trivial sessions block until post-mortem is done.
|
|
9
|
-
# Flag has NO TTL and is NOT deleted on approve.
|
|
10
|
-
# SessionStart hook cleans up the flag for the next session.
|
|
5
|
+
# v6 fix: uses "block" — but blocked ALL sessions including trivial ones.
|
|
6
|
+
# v7 fix: detects trivial sessions (<5 tool calls) and approves immediately.
|
|
7
|
+
# Non-trivial sessions get blocked until post-mortem is done.
|
|
11
8
|
#
|
|
12
9
|
# Flow:
|
|
13
|
-
# Trivial session (quick question, <5
|
|
10
|
+
# Trivial session (quick question, <5 tool calls):
|
|
14
11
|
# → APPROVE immediately, no post-mortem needed
|
|
15
12
|
#
|
|
16
13
|
# Non-trivial session:
|
|
17
14
|
# 1. User closes → hook checks flag → not found → BLOCK
|
|
18
15
|
# 2. AI executes post-mortem → creates flag
|
|
19
16
|
# 3. User closes again → hook sees flag → APPROVE
|
|
20
|
-
# 4. Next session start → SessionStart hook deletes flag
|
|
21
17
|
set -euo pipefail
|
|
22
18
|
|
|
23
|
-
NEXO_HOME="${NEXO_HOME:-$HOME
|
|
24
|
-
NEXO_NAME="${NEXO_NAME:-NEXO}"
|
|
19
|
+
NEXO_HOME="${NEXO_HOME:-$HOME/claude}"
|
|
25
20
|
FLAG_FILE="$NEXO_HOME/operations/.postmortem-complete"
|
|
26
21
|
TODAY=$(date +%Y-%m-%d)
|
|
27
22
|
TOOL_LOG="$NEXO_HOME/operations/tool-logs/${TODAY}.jsonl"
|
|
@@ -29,43 +24,58 @@ TOOL_LOG="$NEXO_HOME/operations/tool-logs/${TODAY}.jsonl"
|
|
|
29
24
|
# 0. Refresh diary draft with latest changes/decisions (best-effort)
|
|
30
25
|
python3 -c "
|
|
31
26
|
import sys, json, os
|
|
32
|
-
sys.path.insert(0, os.
|
|
27
|
+
sys.path.insert(0, os.path.expanduser('~/.nexo/nexo-mcp'))
|
|
33
28
|
os.environ['NEXO_SKIP_FS_INDEX'] = '1'
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
)
|
|
55
|
-
except Exception:
|
|
56
|
-
pass
|
|
29
|
+
from db import init_db, get_db, get_active_sessions, upsert_diary_draft, get_diary_draft
|
|
30
|
+
init_db()
|
|
31
|
+
conn = get_db()
|
|
32
|
+
sessions = get_active_sessions()
|
|
33
|
+
for s in sessions:
|
|
34
|
+
sid = s['sid']
|
|
35
|
+
draft = get_diary_draft(sid)
|
|
36
|
+
if not draft:
|
|
37
|
+
continue
|
|
38
|
+
change_ids = [r[0] for r in conn.execute('SELECT id FROM change_log WHERE session_id = ?', (sid,)).fetchall()]
|
|
39
|
+
decision_ids = [r[0] for r in conn.execute('SELECT id FROM decisions WHERE session_id = ?', (sid,)).fetchall()]
|
|
40
|
+
upsert_diary_draft(
|
|
41
|
+
sid=sid,
|
|
42
|
+
tasks_seen=draft['tasks_seen'],
|
|
43
|
+
change_ids=json.dumps(change_ids),
|
|
44
|
+
decision_ids=json.dumps(decision_ids),
|
|
45
|
+
last_context_hint=draft['last_context_hint'],
|
|
46
|
+
heartbeat_count=draft['heartbeat_count'],
|
|
47
|
+
summary_draft=draft['summary_draft'],
|
|
48
|
+
)
|
|
57
49
|
" 2>/dev/null || true
|
|
58
50
|
|
|
59
|
-
# 1. Detect trivial session — count meaningful tool calls from
|
|
60
|
-
#
|
|
51
|
+
# 1. Detect trivial session — count meaningful tool calls from THIS SESSION only
|
|
52
|
+
# Uses .session-start-ts written by SessionStart hook
|
|
53
|
+
# A session with <5 tool calls (excluding Read/Grep/Glob/Bash) is trivial
|
|
54
|
+
SESSION_START_TS="$NEXO_HOME/operations/.session-start-ts"
|
|
55
|
+
SESSION_START=0
|
|
56
|
+
if [ -f "$SESSION_START_TS" ]; then
|
|
57
|
+
SESSION_START=$(cat "$SESSION_START_TS" 2>/dev/null || echo "0")
|
|
58
|
+
fi
|
|
59
|
+
|
|
61
60
|
TOOL_COUNT=0
|
|
62
61
|
if [ -f "$TOOL_LOG" ]; then
|
|
63
62
|
TOOL_COUNT=$(python3 -c "
|
|
64
|
-
import json, sys
|
|
63
|
+
import json, sys, os
|
|
64
|
+
session_start = float(os.environ.get('SESSION_START', '0'))
|
|
65
65
|
count = 0
|
|
66
66
|
for line in open('$TOOL_LOG'):
|
|
67
67
|
try:
|
|
68
68
|
d = json.loads(line)
|
|
69
|
+
# Only count tools from THIS session (after session-start-ts)
|
|
70
|
+
ts = d.get('timestamp', '')
|
|
71
|
+
if ts and session_start > 0:
|
|
72
|
+
from datetime import datetime
|
|
73
|
+
try:
|
|
74
|
+
entry_ts = datetime.fromisoformat(ts.replace('Z', '+00:00')).timestamp()
|
|
75
|
+
if entry_ts < session_start:
|
|
76
|
+
continue
|
|
77
|
+
except:
|
|
78
|
+
pass
|
|
69
79
|
t = d.get('tool_name', '')
|
|
70
80
|
if t and t not in ('Read', 'Grep', 'Glob', 'Bash', 'ToolSearch'):
|
|
71
81
|
count += 1
|
|
@@ -75,13 +85,8 @@ print(count)
|
|
|
75
85
|
" 2>/dev/null || echo "0")
|
|
76
86
|
fi
|
|
77
87
|
|
|
78
|
-
# Trivial session → approve immediately,
|
|
88
|
+
# Trivial session → approve immediately, no buffer writing, skip post-mortem
|
|
79
89
|
if [ "$TOOL_COUNT" -lt 5 ]; then
|
|
80
|
-
BUFFER="$NEXO_HOME/brain/session_buffer.jsonl"
|
|
81
|
-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S")
|
|
82
|
-
mkdir -p "$(dirname "$BUFFER")"
|
|
83
|
-
echo "{\"ts\":\"$TIMESTAMP\",\"tasks\":[\"trivial session\"],\"decisions\":[],\"user_patterns\":[],\"files_modified\":[],\"errors_resolved\":[],\"self_critique\":\"trivial session — no post-mortem needed\",\"mood\":\"neutral\",\"source\":\"hook-trivial\"}" >> "$BUFFER" 2>/dev/null
|
|
84
|
-
|
|
85
90
|
cat << 'HOOKEOF'
|
|
86
91
|
{
|
|
87
92
|
"decision": "approve"
|
|
@@ -92,8 +97,6 @@ fi
|
|
|
92
97
|
|
|
93
98
|
# 2. Non-trivial session — check if post-mortem was already completed
|
|
94
99
|
# Flag has NO TTL — it persists until SessionStart cleans it up next session.
|
|
95
|
-
# IMPORTANT: do NOT delete flag here — that causes an infinite block loop
|
|
96
|
-
# if the session doesn't close immediately after approve.
|
|
97
100
|
POSTMORTEM_DONE=false
|
|
98
101
|
if [ -f "$FLAG_FILE" ]; then
|
|
99
102
|
POSTMORTEM_DONE=true
|
|
@@ -101,6 +104,7 @@ fi
|
|
|
101
104
|
|
|
102
105
|
if [ "$POSTMORTEM_DONE" = true ]; then
|
|
103
106
|
# Post-mortem was done — allow session to close
|
|
107
|
+
# NOTE: do NOT delete flag here — SessionStart cleans it up next session
|
|
104
108
|
cat << 'HOOKEOF'
|
|
105
109
|
{
|
|
106
110
|
"decision": "approve"
|
|
@@ -108,99 +112,41 @@ if [ "$POSTMORTEM_DONE" = true ]; then
|
|
|
108
112
|
HOOKEOF
|
|
109
113
|
else
|
|
110
114
|
# Post-mortem NOT done — BLOCK session close and inject instructions
|
|
111
|
-
cat << HOOKEOF
|
|
115
|
+
cat << 'HOOKEOF'
|
|
112
116
|
{
|
|
113
117
|
"decision": "block",
|
|
114
|
-
"reason": "STOP HOOK — MANDATORY POST-MORTEM before ending (do NOT ask permission, do NOT skip)
|
|
115
|
-
}
|
|
116
|
-
HOOKEOF
|
|
117
|
-
fi
|
|
118
|
+
"reason": "STOP HOOK — MANDATORY POST-MORTEM before ending (do NOT ask permission, do NOT skip):
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
## 1. SELF-CRITIQUE (MANDATORY — write to session diary)
|
|
121
|
+
Answer these questions in the self_critique field of nexo_session_diary_write:
|
|
122
|
+
- Did the user have to ask me for something I should have detected or done on my own?
|
|
123
|
+
- Did I wait for the user to tell me something I could have verified proactively?
|
|
124
|
+
- Are there systems/states I can check next session without being asked?
|
|
125
|
+
- Did I repeat an error that already had a registered learning?
|
|
126
|
+
- What would I do differently if I repeated this session?
|
|
127
|
+
If any answer is YES — write the specific rule that would prevent repetition.
|
|
128
|
+
If the session was flawless, write 'No self-critique — clean session.'
|
|
122
129
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
import json, sys
|
|
127
|
-
from datetime import datetime, timedelta
|
|
128
|
-
try:
|
|
129
|
-
lines = open('$BUFFER').readlines()
|
|
130
|
-
if lines:
|
|
131
|
-
d = json.loads(lines[-1])
|
|
132
|
-
ts = d.get('ts','')
|
|
133
|
-
src = d.get('source','')
|
|
134
|
-
entry_dt = datetime.strptime(ts, '%Y-%m-%dT%H:%M:%S')
|
|
135
|
-
if datetime.utcnow() - entry_dt < timedelta(seconds=60) and src == 'claude':
|
|
136
|
-
print('skip')
|
|
137
|
-
else:
|
|
138
|
-
print('write')
|
|
139
|
-
else:
|
|
140
|
-
print('write')
|
|
141
|
-
except:
|
|
142
|
-
print('write')
|
|
143
|
-
" 2>/dev/null || echo "write")
|
|
144
|
-
if [ "$LAST_SOURCE" = "skip" ]; then
|
|
145
|
-
SKIP_FALLBACK=true
|
|
146
|
-
fi
|
|
147
|
-
fi
|
|
148
|
-
|
|
149
|
-
if [ "$SKIP_FALLBACK" = false ]; then
|
|
150
|
-
mkdir -p "$(dirname "$BUFFER")"
|
|
151
|
-
ADAPTIVE_MODE="unknown"
|
|
152
|
-
ADAPTIVE_FILE="$NEXO_HOME/brain/adaptive_state.json"
|
|
153
|
-
if [ -f "$ADAPTIVE_FILE" ]; then
|
|
154
|
-
ADAPTIVE_MODE=$(python3 -c "
|
|
155
|
-
import json
|
|
156
|
-
try:
|
|
157
|
-
d = json.load(open('$ADAPTIVE_FILE'))
|
|
158
|
-
print(d.get('current_mode', 'unknown').lower())
|
|
159
|
-
except:
|
|
160
|
-
print('unknown')
|
|
161
|
-
" 2>/dev/null || echo "unknown")
|
|
162
|
-
fi
|
|
163
|
-
echo "{\"ts\":\"$TIMESTAMP\",\"tasks\":[\"session ended\"],\"decisions\":[],\"user_patterns\":[],\"files_modified\":[],\"errors_resolved\":[],\"self_critique\":\"hook-fallback, no self-critique captured\",\"mood\":\"unknown\",\"session_end_mode\":\"$ADAPTIVE_MODE\",\"source\":\"hook-fallback\"}" >> "$BUFFER" 2>/dev/null
|
|
164
|
-
fi
|
|
130
|
+
## 2. SESSION BUFFER
|
|
131
|
+
If the session was NOT trivial, append ONE JSON line to ~/.nexo/brain/session_buffer.jsonl:
|
|
132
|
+
{"ts":"YYYY-MM-DDTHH:MM:SS","tasks":[...],"decisions":[...],"user_patterns":[...],"files_modified":[...],"errors_resolved":[...],"self_critique":"short summary","mood":"focused|impatient|exploratory|frustrated|satisfied|neutral","source":"claude"}
|
|
165
133
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
REFLECTION_STATE="$NEXO_HOME/coordination/reflection-log.json"
|
|
169
|
-
TRIGGER_THRESHOLD=3
|
|
134
|
+
## 3. FOLLOWUPS
|
|
135
|
+
If there were deploys/cron changes/fixes — nexo_followup_create with verification date.
|
|
170
136
|
|
|
171
|
-
|
|
172
|
-
|
|
137
|
+
## 4. PROACTIVE SEEDS
|
|
138
|
+
What can I leave prepared so the next session starts doing useful work without the user asking?
|
|
173
139
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
import json
|
|
179
|
-
from datetime import datetime, timedelta
|
|
180
|
-
try:
|
|
181
|
-
log = json.load(open('$REFLECTION_STATE'))
|
|
182
|
-
if log:
|
|
183
|
-
last = log[-1]['timestamp']
|
|
184
|
-
last_dt = datetime.strptime(last, '%Y-%m-%d %H:%M')
|
|
185
|
-
if datetime.now() - last_dt < timedelta(hours=4):
|
|
186
|
-
print('too_recent')
|
|
187
|
-
else:
|
|
188
|
-
print('ok')
|
|
189
|
-
else:
|
|
190
|
-
print('ok')
|
|
191
|
-
except:
|
|
192
|
-
print('ok')
|
|
193
|
-
" 2>/dev/null)
|
|
194
|
-
if [ "$LAST_TS" = "too_recent" ]; then
|
|
195
|
-
SHOULD_REFLECT=false
|
|
196
|
-
fi
|
|
197
|
-
fi
|
|
140
|
+
## 5. MARK COMPLETE
|
|
141
|
+
When ALL of the above is done, run:
|
|
142
|
+
bash -c 'mkdir -p ~/.nexo/operations && date +%s > ~/.nexo/operations/.postmortem-complete'
|
|
143
|
+
The user will close again and the hook will approve.
|
|
198
144
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
>> "$NEXO_HOME/logs/reflection-stdout.log" \
|
|
203
|
-
2>> "$NEXO_HOME/logs/reflection-stderr.log" &
|
|
204
|
-
fi
|
|
205
|
-
fi
|
|
145
|
+
IMPORTANT: Do NOT say goodbye, do NOT say goodnight or any farewell. Just execute the steps and mark complete."
|
|
146
|
+
}
|
|
147
|
+
HOOKEOF
|
|
206
148
|
fi
|
|
149
|
+
|
|
150
|
+
# 3. Session buffer fallback REMOVED (v8)
|
|
151
|
+
# The old hook-fallback was 86% noise. Session diary (written by Claude during
|
|
152
|
+
# post-mortem) is the only source of truth now. No more buffer writing.
|
package/src/knowledge_graph.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""NEXO Knowledge Graph — Bi-temporal entity-relationship graph on SQLite."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
from datetime import datetime
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
5
|
from typing import Optional
|
|
6
6
|
import os
|
|
7
7
|
|
|
@@ -58,7 +58,7 @@ def upsert_edge(source_type: str, source_ref: str, relation: str,
|
|
|
58
58
|
source_id = source_node["id"]
|
|
59
59
|
target_id = target_node["id"]
|
|
60
60
|
props_json = json.dumps(properties or {})
|
|
61
|
-
now = datetime.now(
|
|
61
|
+
now = datetime.now(timezone.utc).replace(tzinfo=None).strftime("%Y-%m-%dT%H:%M:%S")
|
|
62
62
|
existing = db.execute(
|
|
63
63
|
"SELECT id, weight, confidence, properties FROM kg_edges "
|
|
64
64
|
"WHERE source_id = ? AND target_id = ? AND relation = ? AND valid_until IS NULL",
|
|
@@ -90,7 +90,7 @@ def delete_edge(source_type: str, source_ref: str, relation: str,
|
|
|
90
90
|
target = get_node(target_type, target_ref)
|
|
91
91
|
if not source or not target:
|
|
92
92
|
return False
|
|
93
|
-
now = datetime.now(
|
|
93
|
+
now = datetime.now(timezone.utc).replace(tzinfo=None).strftime("%Y-%m-%dT%H:%M:%S")
|
|
94
94
|
cursor = db.execute(
|
|
95
95
|
"UPDATE kg_edges SET valid_until = ? WHERE source_id = ? AND target_id = ? "
|
|
96
96
|
"AND relation = ? AND valid_until IS NULL",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|