@reconcrap/people-network-memory 0.1.0
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 +476 -0
- package/docs/mcp_tools.md +138 -0
- package/harness_adapters/openclaw/mcp.managed.unix.template.json +25 -0
- package/harness_adapters/openclaw/mcp.managed.windows.template.json +26 -0
- package/harness_adapters/openclaw/mcp.template.json +14 -0
- package/harness_adapters/openclaw/ppl/SKILL.md +114 -0
- package/package.json +30 -0
- package/pyproject.toml +26 -0
- package/scripts/install_windows.ps1 +92 -0
- package/scripts/npm/people-memory.js +276 -0
- package/scripts/people_memory_bootstrap.py +247 -0
- package/scripts/run_graphiti_live_from_liepin.ps1 +87 -0
- package/scripts/run_tests_with_artifacts.ps1 +307 -0
- package/src/people_network_memory/__init__.py +6 -0
- package/src/people_network_memory/application/__init__.py +16 -0
- package/src/people_network_memory/application/normalization.py +1441 -0
- package/src/people_network_memory/application/services.py +921 -0
- package/src/people_network_memory/cli.py +1212 -0
- package/src/people_network_memory/config.py +268 -0
- package/src/people_network_memory/domain/__init__.py +55 -0
- package/src/people_network_memory/domain/identity.py +77 -0
- package/src/people_network_memory/domain/models.py +355 -0
- package/src/people_network_memory/fixtures/__init__.py +6 -0
- package/src/people_network_memory/fixtures/eval.py +398 -0
- package/src/people_network_memory/fixtures/extractor_eval.py +364 -0
- package/src/people_network_memory/fixtures/generator.py +290 -0
- package/src/people_network_memory/fixtures/report.py +252 -0
- package/src/people_network_memory/graphiti_adapter/__init__.py +9 -0
- package/src/people_network_memory/graphiti_adapter/episode_formatter.py +70 -0
- package/src/people_network_memory/graphiti_adapter/graphiti_store.py +655 -0
- package/src/people_network_memory/graphiti_adapter/indexer.py +194 -0
- package/src/people_network_memory/graphiti_adapter/ontology.py +68 -0
- package/src/people_network_memory/harness_adapters/__init__.py +2 -0
- package/src/people_network_memory/harness_adapters/openclaw/__init__.py +9 -0
- package/src/people_network_memory/harness_adapters/openclaw/installer.py +577 -0
- package/src/people_network_memory/harness_adapters/openclaw/integration_eval.py +508 -0
- package/src/people_network_memory/harness_adapters/openclaw/smoke.py +292 -0
- package/src/people_network_memory/infrastructure/__init__.py +2 -0
- package/src/people_network_memory/infrastructure/archive_backup.py +171 -0
- package/src/people_network_memory/infrastructure/diagnostics.py +171 -0
- package/src/people_network_memory/infrastructure/embeddings.py +155 -0
- package/src/people_network_memory/infrastructure/file_store.py +129 -0
- package/src/people_network_memory/infrastructure/graphiti_promotion.py +212 -0
- package/src/people_network_memory/infrastructure/id_generator.py +40 -0
- package/src/people_network_memory/infrastructure/in_memory_store.py +1008 -0
- package/src/people_network_memory/infrastructure/llm_extractor.py +476 -0
- package/src/people_network_memory/infrastructure/llm_identity_advisor.py +200 -0
- package/src/people_network_memory/infrastructure/llm_judge.py +162 -0
- package/src/people_network_memory/infrastructure/redaction.py +21 -0
- package/src/people_network_memory/infrastructure/release_check.py +186 -0
- package/src/people_network_memory/infrastructure/retrieval_intent.py +98 -0
- package/src/people_network_memory/infrastructure/semantic_index.py +262 -0
- package/src/people_network_memory/mcp_server/__init__.py +2 -0
- package/src/people_network_memory/mcp_server/contracts.py +85 -0
- package/src/people_network_memory/mcp_server/runtime.py +133 -0
- package/src/people_network_memory/mcp_server/tools.py +588 -0
- package/src/people_network_memory/ports/__init__.py +2 -0
- package/src/people_network_memory/ports/errors.py +25 -0
- package/src/people_network_memory/ports/interfaces.py +103 -0
- package/src/people_network_memory/projection/__init__.py +6 -0
- package/src/people_network_memory/projection/builders.py +46 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""Stdlib-only bootstrap launcher for AI harnesses.
|
|
2
|
+
|
|
3
|
+
The harness can call this script instead of calling the package directly. It
|
|
4
|
+
creates or repairs a local venv, installs the package, and then launches the MCP
|
|
5
|
+
server through that venv. Keep this file dependency-free.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import platform
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
STATE_SCHEMA_VERSION = 1
|
|
23
|
+
LOCAL_ENV_FILENAME = "local.env.json"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def main(argv: list[str] | None = None) -> int:
|
|
27
|
+
parser = _build_parser()
|
|
28
|
+
args = parser.parse_args(argv)
|
|
29
|
+
repo = Path(args.repo).expanduser().resolve() if args.repo else _repo_root()
|
|
30
|
+
venv = Path(args.venv).expanduser().resolve() if args.venv else repo / ".venv"
|
|
31
|
+
if args.command == "run":
|
|
32
|
+
result = ensure_runtime(
|
|
33
|
+
repo=repo,
|
|
34
|
+
venv=venv,
|
|
35
|
+
extra=args.extra,
|
|
36
|
+
force=args.force,
|
|
37
|
+
git_pull=args.git_pull,
|
|
38
|
+
)
|
|
39
|
+
env = os.environ.copy()
|
|
40
|
+
env.update({key: value for key, value in _load_local_env(repo).items() if key not in env})
|
|
41
|
+
env.setdefault("PEOPLE_MEMORY_BACKEND", args.backend)
|
|
42
|
+
env.setdefault("PEOPLE_MEMORY_SENSITIVITY_POLICY", "personal")
|
|
43
|
+
env.setdefault("GRAPHITI_TELEMETRY_ENABLED", "false")
|
|
44
|
+
passthrough_args = list(args.server_args)
|
|
45
|
+
if passthrough_args and passthrough_args[0] == "--":
|
|
46
|
+
passthrough_args = passthrough_args[1:]
|
|
47
|
+
server_args = ["-m", "people_network_memory.cli", "start", *passthrough_args]
|
|
48
|
+
return subprocess.call([result["python"], *server_args], cwd=str(repo), env=env)
|
|
49
|
+
if args.command in {"ensure", "update"}:
|
|
50
|
+
result = ensure_runtime(
|
|
51
|
+
repo=repo,
|
|
52
|
+
venv=venv,
|
|
53
|
+
extra=args.extra,
|
|
54
|
+
force=args.force or args.command == "update",
|
|
55
|
+
git_pull=args.git_pull,
|
|
56
|
+
)
|
|
57
|
+
print(json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True))
|
|
58
|
+
return 0
|
|
59
|
+
parser.error(f"unsupported command: {args.command}")
|
|
60
|
+
return 2
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def ensure_runtime(
|
|
64
|
+
*,
|
|
65
|
+
repo: Path,
|
|
66
|
+
venv: Path,
|
|
67
|
+
extra: str,
|
|
68
|
+
force: bool = False,
|
|
69
|
+
git_pull: bool = False,
|
|
70
|
+
) -> dict[str, Any]:
|
|
71
|
+
if not (repo / "pyproject.toml").exists():
|
|
72
|
+
raise SystemExit(f"Repo root does not contain pyproject.toml: {repo}")
|
|
73
|
+
git_pull_status = "disabled"
|
|
74
|
+
if git_pull:
|
|
75
|
+
git_pull_status = _git_pull_best_effort(repo)
|
|
76
|
+
venv_python = _venv_python(venv)
|
|
77
|
+
created_venv = False
|
|
78
|
+
if not venv_python.exists():
|
|
79
|
+
_log(f"Creating venv at {venv}")
|
|
80
|
+
_run([sys.executable, "-m", "venv", str(venv)], label="create venv")
|
|
81
|
+
created_venv = True
|
|
82
|
+
state_path = _state_path(repo)
|
|
83
|
+
desired_hash = _desired_state_hash(repo, extra=extra, bootstrap_python=sys.executable)
|
|
84
|
+
current_hash = _read_state_hash(state_path)
|
|
85
|
+
installed = False
|
|
86
|
+
if force or created_venv or current_hash != desired_hash:
|
|
87
|
+
_log("Installing/updating people-network-memory runtime")
|
|
88
|
+
_run([str(venv_python), "-m", "pip", "install", "--upgrade", "pip"], label="upgrade pip")
|
|
89
|
+
target = str(repo) if extra == "base" else f"{repo}[{extra}]"
|
|
90
|
+
_run([str(venv_python), "-m", "pip", "install", "-e", target], label="install package")
|
|
91
|
+
_write_state(
|
|
92
|
+
state_path,
|
|
93
|
+
{
|
|
94
|
+
"schema_version": STATE_SCHEMA_VERSION,
|
|
95
|
+
"state_hash": desired_hash,
|
|
96
|
+
"repo": str(repo),
|
|
97
|
+
"venv": str(venv),
|
|
98
|
+
"extra": extra,
|
|
99
|
+
"bootstrap_python": sys.executable,
|
|
100
|
+
"installed_at": datetime.now(timezone.utc).isoformat(),
|
|
101
|
+
},
|
|
102
|
+
)
|
|
103
|
+
installed = True
|
|
104
|
+
return {
|
|
105
|
+
"ok": True,
|
|
106
|
+
"repo": str(repo),
|
|
107
|
+
"venv": str(venv),
|
|
108
|
+
"python": str(venv_python),
|
|
109
|
+
"extra": extra,
|
|
110
|
+
"created_venv": created_venv,
|
|
111
|
+
"installed_or_updated": installed,
|
|
112
|
+
"git_pull": git_pull,
|
|
113
|
+
"git_pull_status": git_pull_status,
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
118
|
+
parser = argparse.ArgumentParser(description="Bootstrap People Network Memory runtime.")
|
|
119
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
120
|
+
for command in ["run", "ensure", "update"]:
|
|
121
|
+
item = sub.add_parser(command)
|
|
122
|
+
item.add_argument("--repo")
|
|
123
|
+
item.add_argument("--venv")
|
|
124
|
+
item.add_argument("--extra", choices=["base", "graphiti"], default="base")
|
|
125
|
+
item.add_argument("--backend", choices=["local_json", "graphiti", "inmemory"], default="local_json")
|
|
126
|
+
item.add_argument("--force", action="store_true")
|
|
127
|
+
item.add_argument("--git-pull", action="store_true")
|
|
128
|
+
if command == "run":
|
|
129
|
+
item.add_argument("server_args", nargs=argparse.REMAINDER)
|
|
130
|
+
else:
|
|
131
|
+
item.set_defaults(server_args=[])
|
|
132
|
+
return parser
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _repo_root() -> Path:
|
|
136
|
+
return Path(__file__).resolve().parents[1]
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _venv_python(venv: Path) -> Path:
|
|
140
|
+
if platform.system().lower().startswith("win"):
|
|
141
|
+
return venv / "Scripts" / "python.exe"
|
|
142
|
+
return venv / "bin" / "python"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _state_path(repo: Path) -> Path:
|
|
146
|
+
return repo / ".people-network-memory" / "bootstrap-state.json"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _desired_state_hash(repo: Path, *, extra: str, bootstrap_python: str) -> str:
|
|
150
|
+
digest = hashlib.sha256()
|
|
151
|
+
for path in [repo / "pyproject.toml", Path(__file__).resolve()]:
|
|
152
|
+
digest.update(path.name.encode("utf-8"))
|
|
153
|
+
digest.update(path.read_bytes())
|
|
154
|
+
digest.update(extra.encode("utf-8"))
|
|
155
|
+
digest.update(bootstrap_python.encode("utf-8"))
|
|
156
|
+
digest.update(f"{sys.version_info.major}.{sys.version_info.minor}".encode("ascii"))
|
|
157
|
+
return digest.hexdigest()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _read_state_hash(path: Path) -> str | None:
|
|
161
|
+
try:
|
|
162
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
163
|
+
except (FileNotFoundError, json.JSONDecodeError):
|
|
164
|
+
return None
|
|
165
|
+
value = payload.get("state_hash")
|
|
166
|
+
return value if isinstance(value, str) else None
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _write_state(path: Path, payload: dict[str, Any]) -> None:
|
|
170
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
171
|
+
path.write_text(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True), encoding="utf-8")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _load_local_env(repo: Path) -> dict[str, str]:
|
|
175
|
+
path = repo / ".people-network-memory" / LOCAL_ENV_FILENAME
|
|
176
|
+
try:
|
|
177
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
178
|
+
except FileNotFoundError:
|
|
179
|
+
return {}
|
|
180
|
+
except json.JSONDecodeError as exc:
|
|
181
|
+
raise SystemExit(f"Invalid local env JSON at {path}: {exc}") from exc
|
|
182
|
+
if not isinstance(payload, dict):
|
|
183
|
+
raise SystemExit(f"Local env JSON must be an object: {path}")
|
|
184
|
+
loaded: dict[str, str] = {}
|
|
185
|
+
for key, value in payload.items():
|
|
186
|
+
if not _allowed_local_env_name(key):
|
|
187
|
+
continue
|
|
188
|
+
if isinstance(value, str) and value:
|
|
189
|
+
loaded[key] = value
|
|
190
|
+
return loaded
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _allowed_local_env_name(name: str) -> bool:
|
|
194
|
+
return (
|
|
195
|
+
name.startswith("PEOPLE_MEMORY_")
|
|
196
|
+
or name == "GRAPHITI_TELEMETRY_ENABLED"
|
|
197
|
+
or name == "VOLCENGINE_" + "API" + "_KEY"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _git_pull_best_effort(repo: Path) -> str:
|
|
202
|
+
inside_repo = subprocess.run(
|
|
203
|
+
["git", "-C", str(repo), "rev-parse", "--is-inside-work-tree"],
|
|
204
|
+
capture_output=True,
|
|
205
|
+
text=True,
|
|
206
|
+
)
|
|
207
|
+
if inside_repo.returncode != 0:
|
|
208
|
+
_log("Skipping git pull: repo is not a git work tree")
|
|
209
|
+
return "skipped:not_git_repo"
|
|
210
|
+
upstream = subprocess.run(
|
|
211
|
+
["git", "-C", str(repo), "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
|
|
212
|
+
capture_output=True,
|
|
213
|
+
text=True,
|
|
214
|
+
)
|
|
215
|
+
if upstream.returncode != 0:
|
|
216
|
+
_log("Skipping git pull: current branch has no upstream")
|
|
217
|
+
return "skipped:no_upstream"
|
|
218
|
+
command = ["git", "-C", str(repo), "pull", "--ff-only"]
|
|
219
|
+
_log(f"git pull: {' '.join(command)}")
|
|
220
|
+
result = subprocess.run(command, capture_output=True, text=True)
|
|
221
|
+
output = _trim_process_output(result.stdout, result.stderr)
|
|
222
|
+
if output:
|
|
223
|
+
_log(output)
|
|
224
|
+
if result.returncode != 0:
|
|
225
|
+
_log("Git pull failed; continuing with the existing checkout")
|
|
226
|
+
return f"failed:{result.returncode}"
|
|
227
|
+
return "ok"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _trim_process_output(stdout: str, stderr: str, *, limit: int = 800) -> str:
|
|
231
|
+
output = "\n".join(part.strip() for part in [stdout, stderr] if part.strip())
|
|
232
|
+
if len(output) <= limit:
|
|
233
|
+
return output
|
|
234
|
+
return output[:limit] + "... [truncated]"
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _run(command: list[str], *, label: str) -> None:
|
|
238
|
+
_log(f"{label}: {' '.join(command)}")
|
|
239
|
+
subprocess.run(command, check=True)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _log(message: str) -> None:
|
|
243
|
+
print(f"[people-memory-bootstrap] {message}", file=sys.stderr, flush=True)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
if __name__ == "__main__":
|
|
247
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[string]$ArtifactsDir = ".people-network-memory/test-artifacts/graphiti-live",
|
|
3
|
+
[string]$LiepinConfigPath = "$HOME/.liepin-recommend-mcp/screening-config.json",
|
|
4
|
+
[string]$PythonCommand = "python"
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
$ErrorActionPreference = "Stop"
|
|
8
|
+
|
|
9
|
+
$repoRoot = Split-Path -Parent $PSScriptRoot
|
|
10
|
+
Set-Location $repoRoot
|
|
11
|
+
|
|
12
|
+
$localEnvPath = Join-Path $repoRoot ".people-network-memory/local.env.json"
|
|
13
|
+
if (Test-Path -LiteralPath $localEnvPath) {
|
|
14
|
+
$localEnv = Get-Content -LiteralPath $localEnvPath -Raw | ConvertFrom-Json
|
|
15
|
+
foreach ($property in $localEnv.PSObject.Properties) {
|
|
16
|
+
$name = $property.Name
|
|
17
|
+
$isAllowed = (
|
|
18
|
+
$name.StartsWith("PEOPLE_MEMORY_") -or
|
|
19
|
+
$name -eq "GRAPHITI_TELEMETRY_ENABLED" -or
|
|
20
|
+
$name -eq "VOLCENGINE_API_KEY"
|
|
21
|
+
)
|
|
22
|
+
if ($isAllowed -and -not [System.Environment]::GetEnvironmentVariable($name, "Process")) {
|
|
23
|
+
[System.Environment]::SetEnvironmentVariable($name, [string]$property.Value, "Process")
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
$resolvedLiepinConfigPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(
|
|
29
|
+
$LiepinConfigPath
|
|
30
|
+
)
|
|
31
|
+
if (-not (Test-Path -LiteralPath $resolvedLiepinConfigPath)) {
|
|
32
|
+
throw "Liepin LLM config not found: $resolvedLiepinConfigPath"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
$liepinConfig = Get-Content -LiteralPath $resolvedLiepinConfigPath -Raw | ConvertFrom-Json
|
|
36
|
+
foreach ($field in @("baseUrl", "apiKey", "model")) {
|
|
37
|
+
if (-not $liepinConfig.PSObject.Properties.Name.Contains($field) -or -not $liepinConfig.$field) {
|
|
38
|
+
throw "Liepin LLM config is missing required field: $field"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (-not $env:PEOPLE_MEMORY_EMBEDDING_API_KEY -and $env:VOLCENGINE_API_KEY) {
|
|
43
|
+
$env:PEOPLE_MEMORY_EMBEDDING_API_KEY = $env:VOLCENGINE_API_KEY
|
|
44
|
+
}
|
|
45
|
+
if (-not $env:PEOPLE_MEMORY_EMBEDDING_API_KEY) {
|
|
46
|
+
throw "Set PEOPLE_MEMORY_EMBEDDING_API_KEY or VOLCENGINE_API_KEY before running live Graphiti artifacts."
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
$env:PEOPLE_MEMORY_BACKEND = "graphiti"
|
|
50
|
+
$env:PEOPLE_MEMORY_GRAPH_BACKEND = "kuzu"
|
|
51
|
+
$env:PEOPLE_MEMORY_EMBEDDING_PROVIDER = "volcengine"
|
|
52
|
+
$env:PEOPLE_MEMORY_EMBEDDING_BASE_URL = "https://ark.cn-beijing.volces.com/api/coding/v3"
|
|
53
|
+
$env:PEOPLE_MEMORY_EMBEDDING_MODEL = "doubao-embedding-vision-251215"
|
|
54
|
+
$env:PEOPLE_MEMORY_LLM_PROVIDER = "openai_compatible"
|
|
55
|
+
$env:PEOPLE_MEMORY_LLM_BASE_URL = $liepinConfig.baseUrl
|
|
56
|
+
$env:PEOPLE_MEMORY_LLM_MODEL = $liepinConfig.model
|
|
57
|
+
$env:PEOPLE_MEMORY_LLM_API_KEY = $liepinConfig.apiKey
|
|
58
|
+
$env:PEOPLE_MEMORY_LLM_RESPONSE_FORMAT = "none"
|
|
59
|
+
$env:GRAPHITI_TELEMETRY_ENABLED = "false"
|
|
60
|
+
|
|
61
|
+
& powershell -ExecutionPolicy Bypass -File .\scripts\run_tests_with_artifacts.ps1 `
|
|
62
|
+
-ArtifactsDir $ArtifactsDir `
|
|
63
|
+
-PythonCommand $PythonCommand `
|
|
64
|
+
-Graphiti
|
|
65
|
+
$runnerExitCode = $LASTEXITCODE
|
|
66
|
+
if ($runnerExitCode -ne 0) {
|
|
67
|
+
exit $runnerExitCode
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
$resolvedArtifactsDir = if ([System.IO.Path]::IsPathRooted($ArtifactsDir)) {
|
|
71
|
+
[System.IO.Path]::GetFullPath($ArtifactsDir)
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
[System.IO.Path]::GetFullPath((Join-Path $repoRoot $ArtifactsDir))
|
|
75
|
+
}
|
|
76
|
+
$manifestPath = Join-Path $resolvedArtifactsDir "manifest.json"
|
|
77
|
+
$manifest = Get-Content -LiteralPath $manifestPath -Raw | ConvertFrom-Json
|
|
78
|
+
$failedGraphiti = @(
|
|
79
|
+
$manifest.steps | Where-Object { $_.name -like "graphiti_*" -and $_.exit_code -ne 0 }
|
|
80
|
+
)
|
|
81
|
+
if ($failedGraphiti.Count -gt 0) {
|
|
82
|
+
Write-Host "Failed live Graphiti steps:"
|
|
83
|
+
$failedGraphiti | ForEach-Object { Write-Host " - $($_.name): exit $($_.exit_code)" }
|
|
84
|
+
exit 1
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
exit 0
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[string]$ArtifactsDir = ".people-network-memory/test-artifacts/latest",
|
|
3
|
+
[string]$PythonCommand = "python",
|
|
4
|
+
[switch]$Graphiti,
|
|
5
|
+
[switch]$LiveLlm
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
$ErrorActionPreference = "Stop"
|
|
9
|
+
|
|
10
|
+
$repoRoot = Split-Path -Parent $PSScriptRoot
|
|
11
|
+
Set-Location $repoRoot
|
|
12
|
+
|
|
13
|
+
$repoRootFull = [System.IO.Path]::GetFullPath($repoRoot)
|
|
14
|
+
$resolvedArtifactsDir = [System.IO.Path]::GetFullPath((Join-Path $repoRoot $ArtifactsDir))
|
|
15
|
+
if (-not $resolvedArtifactsDir.StartsWith($repoRootFull, [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
16
|
+
throw "ArtifactsDir must resolve inside the repository root."
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if ($PythonCommand -eq "python") {
|
|
20
|
+
$venvPython = Join-Path $repoRoot ".venv/Scripts/python.exe"
|
|
21
|
+
if (Test-Path -LiteralPath $venvPython) {
|
|
22
|
+
$PythonCommand = $venvPython
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (Test-Path -LiteralPath $resolvedArtifactsDir) {
|
|
27
|
+
Get-ChildItem -LiteralPath $resolvedArtifactsDir -Force | Remove-Item -Recurse -Force
|
|
28
|
+
}
|
|
29
|
+
New-Item -ItemType Directory -Force -Path $resolvedArtifactsDir | Out-Null
|
|
30
|
+
|
|
31
|
+
$manifest = [ordered]@{
|
|
32
|
+
schema_version = 1
|
|
33
|
+
created_at = (Get-Date).ToUniversalTime().ToString("o")
|
|
34
|
+
repo_root = $repoRoot
|
|
35
|
+
artifacts_dir = $resolvedArtifactsDir
|
|
36
|
+
steps = @()
|
|
37
|
+
}
|
|
38
|
+
$manifestPath = Join-Path $resolvedArtifactsDir "manifest.json"
|
|
39
|
+
$script:LastArtifactStepExitCode = $null
|
|
40
|
+
|
|
41
|
+
function Save-Manifest {
|
|
42
|
+
$manifest | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $manifestPath -Encoding UTF8
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function Invoke-ArtifactStep {
|
|
46
|
+
param(
|
|
47
|
+
[string]$Name,
|
|
48
|
+
[string]$Command,
|
|
49
|
+
[string[]]$Arguments,
|
|
50
|
+
[string]$OutputFile,
|
|
51
|
+
[switch]$AllowFailure,
|
|
52
|
+
[switch]$CleanPeopleMemoryEnv
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
$outputPath = Join-Path $resolvedArtifactsDir $OutputFile
|
|
56
|
+
$startedAt = (Get-Date).ToUniversalTime().ToString("o")
|
|
57
|
+
Write-Host ""
|
|
58
|
+
Write-Host "==> $Name"
|
|
59
|
+
Write-Host " $Command $($Arguments -join ' ')"
|
|
60
|
+
$step = [ordered]@{
|
|
61
|
+
name = $Name
|
|
62
|
+
command = $Command
|
|
63
|
+
arguments = $Arguments
|
|
64
|
+
output = $outputPath
|
|
65
|
+
exit_code = $null
|
|
66
|
+
allow_failure = [bool]$AllowFailure
|
|
67
|
+
clean_people_memory_env = [bool]$CleanPeopleMemoryEnv
|
|
68
|
+
started_at = $startedAt
|
|
69
|
+
finished_at = $null
|
|
70
|
+
status = "running"
|
|
71
|
+
}
|
|
72
|
+
$manifest.steps += $step
|
|
73
|
+
Save-Manifest
|
|
74
|
+
|
|
75
|
+
$previousErrorActionPreference = $ErrorActionPreference
|
|
76
|
+
$envSnapshot = @{}
|
|
77
|
+
if ($CleanPeopleMemoryEnv) {
|
|
78
|
+
$namesToClean = @(
|
|
79
|
+
Get-ChildItem Env: |
|
|
80
|
+
Where-Object { $_.Name -like "PEOPLE_MEMORY_*" } |
|
|
81
|
+
ForEach-Object { $_.Name }
|
|
82
|
+
)
|
|
83
|
+
$namesToClean += "GRAPHITI_TELEMETRY_ENABLED"
|
|
84
|
+
$namesToClean = @($namesToClean | Select-Object -Unique)
|
|
85
|
+
foreach ($envVarName in $namesToClean) {
|
|
86
|
+
$envSnapshot[$envVarName] = [System.Environment]::GetEnvironmentVariable($envVarName, "Process")
|
|
87
|
+
[System.Environment]::SetEnvironmentVariable($envVarName, $null, "Process")
|
|
88
|
+
}
|
|
89
|
+
$envSnapshot["PEOPLE_MEMORY_DISABLE_LOCAL_ENV"] = [System.Environment]::GetEnvironmentVariable("PEOPLE_MEMORY_DISABLE_LOCAL_ENV", "Process")
|
|
90
|
+
[System.Environment]::SetEnvironmentVariable("PEOPLE_MEMORY_DISABLE_LOCAL_ENV", "true", "Process")
|
|
91
|
+
$env:GRAPHITI_TELEMETRY_ENABLED = "false"
|
|
92
|
+
}
|
|
93
|
+
$ErrorActionPreference = "Continue"
|
|
94
|
+
try {
|
|
95
|
+
& $Command @Arguments *> $outputPath
|
|
96
|
+
$exitCode = $LASTEXITCODE
|
|
97
|
+
if ($null -eq $exitCode) {
|
|
98
|
+
$exitCode = 0
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
$ErrorActionPreference = $previousErrorActionPreference
|
|
103
|
+
if ($CleanPeopleMemoryEnv) {
|
|
104
|
+
foreach ($envVarName in $envSnapshot.Keys) {
|
|
105
|
+
[System.Environment]::SetEnvironmentVariable($envVarName, $envSnapshot[$envVarName], "Process")
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
$finishedAt = (Get-Date).ToUniversalTime().ToString("o")
|
|
110
|
+
|
|
111
|
+
$step["exit_code"] = $exitCode
|
|
112
|
+
$step["finished_at"] = $finishedAt
|
|
113
|
+
$step["status"] = "completed"
|
|
114
|
+
$script:LastArtifactStepExitCode = $exitCode
|
|
115
|
+
Save-Manifest
|
|
116
|
+
|
|
117
|
+
if ($exitCode -ne 0 -and -not $AllowFailure) {
|
|
118
|
+
Write-Host " failed with exit code $exitCode"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function Add-SkippedArtifactStep {
|
|
123
|
+
param(
|
|
124
|
+
[string]$Name,
|
|
125
|
+
[string]$Reason
|
|
126
|
+
)
|
|
127
|
+
$now = (Get-Date).ToUniversalTime().ToString("o")
|
|
128
|
+
$manifest.steps += [ordered]@{
|
|
129
|
+
name = $Name
|
|
130
|
+
command = ""
|
|
131
|
+
arguments = @()
|
|
132
|
+
output = ""
|
|
133
|
+
exit_code = 0
|
|
134
|
+
allow_failure = $true
|
|
135
|
+
clean_people_memory_env = $false
|
|
136
|
+
started_at = $now
|
|
137
|
+
finished_at = $now
|
|
138
|
+
status = "skipped"
|
|
139
|
+
reason = $Reason
|
|
140
|
+
}
|
|
141
|
+
Save-Manifest
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
Invoke-ArtifactStep `
|
|
145
|
+
-Name "unit_tests" `
|
|
146
|
+
-Command $PythonCommand `
|
|
147
|
+
-Arguments @("-m", "unittest", "discover", "-s", "tests", "-v") `
|
|
148
|
+
-OutputFile "unittest.txt" `
|
|
149
|
+
-CleanPeopleMemoryEnv
|
|
150
|
+
|
|
151
|
+
Invoke-ArtifactStep `
|
|
152
|
+
-Name "mcp_test_mode_start" `
|
|
153
|
+
-Command $PythonCommand `
|
|
154
|
+
-Arguments @("-m", "people_network_memory.cli", "start", "--test-mode", "--once") `
|
|
155
|
+
-OutputFile "mcp-start.json" `
|
|
156
|
+
-CleanPeopleMemoryEnv
|
|
157
|
+
|
|
158
|
+
Invoke-ArtifactStep `
|
|
159
|
+
-Name "doctor_test_mode" `
|
|
160
|
+
-Command $PythonCommand `
|
|
161
|
+
-Arguments @("-m", "people_network_memory.cli", "doctor", "--test-mode") `
|
|
162
|
+
-OutputFile "doctor-test-mode.json" `
|
|
163
|
+
-CleanPeopleMemoryEnv
|
|
164
|
+
|
|
165
|
+
Invoke-ArtifactStep `
|
|
166
|
+
-Name "tool_schemas" `
|
|
167
|
+
-Command $PythonCommand `
|
|
168
|
+
-Arguments @("-m", "people_network_memory.cli", "tool-schemas") `
|
|
169
|
+
-OutputFile "tool-schemas.json" `
|
|
170
|
+
-CleanPeopleMemoryEnv
|
|
171
|
+
|
|
172
|
+
Invoke-ArtifactStep `
|
|
173
|
+
-Name "fixture_eval" `
|
|
174
|
+
-Command $PythonCommand `
|
|
175
|
+
-Arguments @(
|
|
176
|
+
"-m", "people_network_memory.cli", "eval-fixtures",
|
|
177
|
+
"--include-cases",
|
|
178
|
+
"--output", (Join-Path $resolvedArtifactsDir "eval-fixtures.json")
|
|
179
|
+
) `
|
|
180
|
+
-OutputFile "eval-fixtures.stdout.json" `
|
|
181
|
+
-CleanPeopleMemoryEnv
|
|
182
|
+
|
|
183
|
+
Invoke-ArtifactStep `
|
|
184
|
+
-Name "fixture_eval_report" `
|
|
185
|
+
-Command $PythonCommand `
|
|
186
|
+
-Arguments @(
|
|
187
|
+
"-m", "people_network_memory.cli", "report-eval",
|
|
188
|
+
"--input", (Join-Path $resolvedArtifactsDir "eval-fixtures.json"),
|
|
189
|
+
"--output", (Join-Path $resolvedArtifactsDir "eval-fixtures-report.md"),
|
|
190
|
+
"--title", "Fixture Retrieval Test Report"
|
|
191
|
+
) `
|
|
192
|
+
-OutputFile "eval-fixtures-report.stdout.json" `
|
|
193
|
+
-CleanPeopleMemoryEnv
|
|
194
|
+
|
|
195
|
+
Invoke-ArtifactStep `
|
|
196
|
+
-Name "openclaw_smoke" `
|
|
197
|
+
-Command $PythonCommand `
|
|
198
|
+
-Arguments @("-m", "people_network_memory.cli", "smoke-openclaw") `
|
|
199
|
+
-OutputFile "smoke-openclaw.json" `
|
|
200
|
+
-CleanPeopleMemoryEnv
|
|
201
|
+
|
|
202
|
+
Invoke-ArtifactStep `
|
|
203
|
+
-Name "harness_integration_eval" `
|
|
204
|
+
-Command $PythonCommand `
|
|
205
|
+
-Arguments @(
|
|
206
|
+
"-m", "people_network_memory.cli", "eval-harness-integration",
|
|
207
|
+
"--output", (Join-Path $resolvedArtifactsDir "harness-integration-eval.json")
|
|
208
|
+
) `
|
|
209
|
+
-OutputFile "harness-integration-eval.stdout.json" `
|
|
210
|
+
-CleanPeopleMemoryEnv
|
|
211
|
+
|
|
212
|
+
Invoke-ArtifactStep `
|
|
213
|
+
-Name "release_check" `
|
|
214
|
+
-Command $PythonCommand `
|
|
215
|
+
-Arguments @(
|
|
216
|
+
"-m", "people_network_memory.cli", "release-check",
|
|
217
|
+
"--output", (Join-Path $resolvedArtifactsDir "release-check.json")
|
|
218
|
+
) `
|
|
219
|
+
-OutputFile "release-check.stdout.json" `
|
|
220
|
+
-CleanPeopleMemoryEnv
|
|
221
|
+
|
|
222
|
+
if ($LiveLlm) {
|
|
223
|
+
Invoke-ArtifactStep `
|
|
224
|
+
-Name "llm_extractor_eval" `
|
|
225
|
+
-Command $PythonCommand `
|
|
226
|
+
-Arguments @(
|
|
227
|
+
"-m", "people_network_memory.cli", "eval-extractor",
|
|
228
|
+
"--output", (Join-Path $resolvedArtifactsDir "llm-extractor-eval.json")
|
|
229
|
+
) `
|
|
230
|
+
-OutputFile "llm-extractor-eval.stdout.json"
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
Invoke-ArtifactStep `
|
|
234
|
+
-Name "git_diff_check" `
|
|
235
|
+
-Command "git" `
|
|
236
|
+
-Arguments @("diff", "--check") `
|
|
237
|
+
-OutputFile "git-diff-check.txt"
|
|
238
|
+
|
|
239
|
+
Invoke-ArtifactStep `
|
|
240
|
+
-Name "git_status" `
|
|
241
|
+
-Command "git" `
|
|
242
|
+
-Arguments @("status", "--short") `
|
|
243
|
+
-OutputFile "git-status.txt"
|
|
244
|
+
|
|
245
|
+
if ($Graphiti) {
|
|
246
|
+
Invoke-ArtifactStep `
|
|
247
|
+
-Name "graphiti_embedding_check" `
|
|
248
|
+
-Command $PythonCommand `
|
|
249
|
+
-Arguments @("-m", "people_network_memory.cli", "check-embedding") `
|
|
250
|
+
-OutputFile "embedding-check.json" `
|
|
251
|
+
-AllowFailure
|
|
252
|
+
|
|
253
|
+
Invoke-ArtifactStep `
|
|
254
|
+
-Name "graphiti_spike" `
|
|
255
|
+
-Command $PythonCommand `
|
|
256
|
+
-Arguments @("-m", "people_network_memory.cli", "spike-graphiti") `
|
|
257
|
+
-OutputFile "graphiti-spike.json" `
|
|
258
|
+
-AllowFailure
|
|
259
|
+
|
|
260
|
+
Invoke-ArtifactStep `
|
|
261
|
+
-Name "graphiti_promotion_gate" `
|
|
262
|
+
-Command $PythonCommand `
|
|
263
|
+
-Arguments @(
|
|
264
|
+
"-m", "people_network_memory.cli", "graphiti-gate",
|
|
265
|
+
"--isolated",
|
|
266
|
+
"--include-cases",
|
|
267
|
+
"--all-cases",
|
|
268
|
+
"--output", (Join-Path $resolvedArtifactsDir "graphiti-gate.json")
|
|
269
|
+
) `
|
|
270
|
+
-OutputFile "graphiti-gate.stdout.json" `
|
|
271
|
+
-AllowFailure
|
|
272
|
+
|
|
273
|
+
if ($script:LastArtifactStepExitCode -eq 0) {
|
|
274
|
+
Invoke-ArtifactStep `
|
|
275
|
+
-Name "graphiti_eval_smoke" `
|
|
276
|
+
-Command $PythonCommand `
|
|
277
|
+
-Arguments @(
|
|
278
|
+
"-m", "people_network_memory.cli", "eval-graphiti",
|
|
279
|
+
"--isolated",
|
|
280
|
+
"--max-interactions", "2",
|
|
281
|
+
"--max-queries", "2",
|
|
282
|
+
"--output", (Join-Path $resolvedArtifactsDir "graphiti-eval.json")
|
|
283
|
+
) `
|
|
284
|
+
-OutputFile "graphiti-eval.stdout.json" `
|
|
285
|
+
-AllowFailure
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
Add-SkippedArtifactStep `
|
|
289
|
+
-Name "graphiti_eval_smoke" `
|
|
290
|
+
-Reason "Skipped because graphiti_promotion_gate did not pass."
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
Save-Manifest
|
|
295
|
+
|
|
296
|
+
$failed = @($manifest.steps | Where-Object { $null -ne $_.exit_code -and $_.exit_code -ne 0 -and -not $_.allow_failure })
|
|
297
|
+
Write-Host ""
|
|
298
|
+
Write-Host "Artifacts saved to $resolvedArtifactsDir"
|
|
299
|
+
Write-Host "Manifest: $manifestPath"
|
|
300
|
+
|
|
301
|
+
if ($failed.Count -gt 0) {
|
|
302
|
+
Write-Host "Failed required steps:"
|
|
303
|
+
$failed | ForEach-Object { Write-Host " - $($_.name): exit $($_.exit_code)" }
|
|
304
|
+
exit 1
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
Write-Host "All required artifact steps passed."
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Application use cases."""
|
|
2
|
+
|
|
3
|
+
from people_network_memory.application.services import (
|
|
4
|
+
BuildPersonCardService,
|
|
5
|
+
RecordInteractionService,
|
|
6
|
+
ResolveIdentityService,
|
|
7
|
+
RetrieveContextService,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"BuildPersonCardService",
|
|
12
|
+
"RecordInteractionService",
|
|
13
|
+
"ResolveIdentityService",
|
|
14
|
+
"RetrieveContextService",
|
|
15
|
+
]
|
|
16
|
+
|