@pentatonic-ai/ai-agent-sdk 0.6.0 → 0.7.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.
Files changed (92) hide show
  1. package/README.md +178 -69
  2. package/bin/__tests__/callback-server.test.js +4 -1
  3. package/bin/cli.js +41 -164
  4. package/bin/commands/config.js +251 -0
  5. package/bin/commands/login.js +10 -3
  6. package/package.json +2 -1
  7. package/packages/doctor/__tests__/detect.test.js +2 -6
  8. package/packages/doctor/src/checks/local-memory.js +164 -196
  9. package/packages/doctor/src/detect.js +11 -3
  10. package/packages/memory/src/corpus/adapters.js +104 -0
  11. package/packages/memory/src/corpus/cli.js +72 -7
  12. package/packages/memory/src/corpus/index.js +1 -1
  13. package/packages/memory-engine/.env.example +13 -0
  14. package/packages/memory-engine/README.md +131 -0
  15. package/packages/memory-engine/bench/README.md +99 -0
  16. package/packages/memory-engine/bench/scorecards-engine/agent-coding__pentatonic-baseline__20260427-142523.json +1115 -0
  17. package/packages/memory-engine/bench/scorecards-engine/chat-recall__pentatonic-baseline__20260427-142648.json +819 -0
  18. package/packages/memory-engine/bench/scorecards-engine/circular-economy__pentatonic-baseline__20260427-142757.json +1278 -0
  19. package/packages/memory-engine/bench/scorecards-engine/customer-support__pentatonic-baseline__20260427-142900.json +1018 -0
  20. package/packages/memory-engine/bench/scorecards-engine/marketplace-ops__pentatonic-baseline__20260427-142957.json +1038 -0
  21. package/packages/memory-engine/bench/scorecards-engine/product-catalogue__pentatonic-baseline__20260427-143122.json +961 -0
  22. package/packages/memory-engine/bench/scorecards-engine-via-docker/agent-coding__pentatonic-memory__20260427-161812.json +1115 -0
  23. package/packages/memory-engine/bench/scorecards-engine-via-docker/chat-recall__pentatonic-memory__20260427-161701.json +819 -0
  24. package/packages/memory-engine/bench/scorecards-engine-via-docker/circular-economy__pentatonic-memory__20260427-161713.json +1278 -0
  25. package/packages/memory-engine/bench/scorecards-engine-via-docker/customer-support__pentatonic-memory__20260427-161723.json +1018 -0
  26. package/packages/memory-engine/bench/scorecards-engine-via-docker/marketplace-ops__pentatonic-memory__20260427-161732.json +1038 -0
  27. package/packages/memory-engine/bench/scorecards-engine-via-docker/product-catalogue__pentatonic-memory__20260427-161741.json +937 -0
  28. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/agent-coding__pentatonic-memory__20260427-184718.json +1115 -0
  29. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/chat-recall__pentatonic-memory__20260427-184614.json +819 -0
  30. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/circular-economy__pentatonic-memory__20260427-184809.json +1278 -0
  31. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/customer-support__pentatonic-memory__20260427-184854.json +1018 -0
  32. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/marketplace-ops__pentatonic-memory__20260427-184929.json +1038 -0
  33. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/product-catalogue__pentatonic-memory__20260427-185015.json +961 -0
  34. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/agent-coding__pentatonic-memory__20260427-175252.json +1115 -0
  35. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/chat-recall__pentatonic-memory__20260427-175312.json +819 -0
  36. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/circular-economy__pentatonic-memory__20260427-175335.json +1278 -0
  37. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/customer-support__pentatonic-memory__20260427-175355.json +1018 -0
  38. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/marketplace-ops__pentatonic-memory__20260427-175413.json +1038 -0
  39. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/product-catalogue__pentatonic-memory__20260427-175430.json +883 -0
  40. package/packages/memory-engine/bench/scorecards-engine-via-shim/agent-coding__pentatonic-memory__20260427-155409.json +1115 -0
  41. package/packages/memory-engine/bench/scorecards-engine-via-shim/chat-recall__pentatonic-memory__20260427-155421.json +819 -0
  42. package/packages/memory-engine/bench/scorecards-engine-via-shim/circular-economy__pentatonic-memory__20260427-155433.json +1278 -0
  43. package/packages/memory-engine/bench/scorecards-engine-via-shim/customer-support__pentatonic-memory__20260427-155443.json +1018 -0
  44. package/packages/memory-engine/bench/scorecards-engine-via-shim/marketplace-ops__pentatonic-memory__20260427-155453.json +1038 -0
  45. package/packages/memory-engine/bench/scorecards-engine-via-shim/product-catalogue__pentatonic-memory__20260427-155503.json +937 -0
  46. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/agent-coding__pentatonic-memory-latest__20260427-145103.json +1115 -0
  47. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/agent-coding__pentatonic-memory__20260427-144909.json +1115 -0
  48. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/chat-recall__pentatonic-memory-latest__20260427-145153.json +819 -0
  49. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/chat-recall__pentatonic-memory__20260427-145120.json +542 -0
  50. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/circular-economy__pentatonic-memory-latest__20260427-145313.json +1278 -0
  51. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/circular-economy__pentatonic-memory__20260427-145207.json +894 -0
  52. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/customer-support__pentatonic-memory-latest__20260427-145412.json +1018 -0
  53. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/customer-support__pentatonic-memory__20260427-145327.json +680 -0
  54. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/marketplace-ops__pentatonic-memory-latest__20260427-145517.json +1038 -0
  55. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/marketplace-ops__pentatonic-memory__20260427-145422.json +693 -0
  56. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/product-catalogue__pentatonic-memory-latest__20260427-145616.json +961 -0
  57. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/product-catalogue__pentatonic-memory__20260427-145528.json +727 -0
  58. package/packages/memory-engine/compat/Dockerfile +11 -0
  59. package/packages/memory-engine/compat/server.py +680 -0
  60. package/packages/memory-engine/docker-compose.yml +243 -0
  61. package/packages/memory-engine/engine/README.md +52 -0
  62. package/packages/memory-engine/engine/l2-hybridrag-proxy.py +1543 -0
  63. package/packages/memory-engine/engine/l5-comms-layer.py +663 -0
  64. package/packages/memory-engine/engine/l6-document-store.py +1018 -0
  65. package/packages/memory-engine/engine/services/l2/Dockerfile +41 -0
  66. package/packages/memory-engine/engine/services/l2/init_databases.py +81 -0
  67. package/packages/memory-engine/engine/services/l2/l2-hybridrag-proxy.py +1543 -0
  68. package/packages/memory-engine/engine/services/l4/Dockerfile +15 -0
  69. package/packages/memory-engine/engine/services/l4/server.py +265 -0
  70. package/packages/memory-engine/engine/services/l5/Dockerfile +9 -0
  71. package/packages/memory-engine/engine/services/l5/l5-comms-layer.py +696 -0
  72. package/packages/memory-engine/engine/services/l6/Dockerfile +11 -0
  73. package/packages/memory-engine/engine/services/l6/l6-document-store.py +1035 -0
  74. package/packages/memory-engine/engine/services/nv-embed/Dockerfile +28 -0
  75. package/packages/memory-engine/engine/services/nv-embed/server.py +152 -0
  76. package/packages/memory-engine/pme_memory/__init__.py +0 -0
  77. package/packages/memory-engine/pme_memory/__main__.py +129 -0
  78. package/packages/memory-engine/pme_memory/artifacts.py +95 -0
  79. package/packages/memory-engine/pme_memory/embed.py +74 -0
  80. package/packages/memory-engine/pme_memory/health.py +36 -0
  81. package/packages/memory-engine/pme_memory/hygiene.py +159 -0
  82. package/packages/memory-engine/pme_memory/indexer.py +200 -0
  83. package/packages/memory-engine/pme_memory/needs.py +55 -0
  84. package/packages/memory-engine/pme_memory/provenance.py +80 -0
  85. package/packages/memory-engine/pme_memory/scoring.py +168 -0
  86. package/packages/memory-engine/pme_memory/search.py +52 -0
  87. package/packages/memory-engine/pme_memory/store.py +86 -0
  88. package/packages/memory-engine/pme_memory/synthesis.py +114 -0
  89. package/packages/memory-engine/pyproject.toml +65 -0
  90. package/packages/memory-engine/scripts/kg-extractor.py +557 -0
  91. package/packages/memory-engine/scripts/kg-preflexor-v2.py +738 -0
  92. package/packages/memory-engine/tests/test_api_contract.sh +57 -0
@@ -0,0 +1,15 @@
1
+ FROM python:3.12-slim
2
+
3
+ WORKDIR /app
4
+
5
+ RUN pip install --no-cache-dir fastapi 'uvicorn[standard]' httpx pydantic
6
+
7
+ COPY server.py /app/server.py
8
+
9
+ RUN mkdir -p /data
10
+ ENV L4_DB_PATH=/data/vec.db
11
+ ENV PORT=8042
12
+
13
+ EXPOSE 8042
14
+
15
+ CMD ["python", "server.py", "--port", "8042"]
@@ -0,0 +1,265 @@
1
+ """
2
+ L4 sqlite-vec sidecar.
3
+
4
+ Vector index sidecar for the Pentatonic Memory Engine stack.
5
+ Exposes /health, /search, /index-batch, /refresh over HTTP.
6
+
7
+ Endpoints:
8
+ GET /health
9
+ POST /search body: {"query":"...", "limit":10}
10
+ POST /index-batch body: {"records":[{"id","text"}, ...]}
11
+ POST /refresh no-op (sqlite-vec writes are immediate)
12
+
13
+ Env:
14
+ L4_DB_PATH default /data/vec.db
15
+ L4_NV_EMBED_URL default http://nv-embed:8041/v1/embeddings
16
+ PORT default 8042
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import argparse
22
+ import hashlib
23
+ import os
24
+ import sqlite3
25
+ import struct
26
+ import time
27
+ from pathlib import Path
28
+ from typing import Any
29
+
30
+ import httpx
31
+ from fastapi import FastAPI, HTTPException
32
+ from pydantic import BaseModel
33
+
34
+ # ----------------------------------------------------------------------
35
+ # Config
36
+ # ----------------------------------------------------------------------
37
+
38
+ DB_PATH = os.environ.get("L4_DB_PATH", "/data/vec.db")
39
+ NV_EMBED_URL = os.environ.get("L4_NV_EMBED_URL", "http://nv-embed:8041/v1/embeddings")
40
+ # Embedding model name sent in /v1/embeddings request body. Defaults to
41
+ # the production NV-Embed-v2 name; override via env when pointing at a
42
+ # different OpenAI-compat endpoint (e.g. Ollama with nomic-embed-text).
43
+ EMBED_MODEL_NAME = os.environ.get("L4_EMBED_MODEL", "nv-embed-v2")
44
+ # Optional Authorization: Bearer <key> for the embedding endpoint.
45
+ # Set when calling a hosted gateway (e.g. pentatonic-ai-gateway). Empty = no auth.
46
+ EMBED_API_KEY = os.environ.get("L4_EMBED_API_KEY", "")
47
+ EMBED_DIM = int(os.environ.get("L4_EMBED_DIM", "4096"))
48
+
49
+
50
+
51
+ # ----------------------------------------------------------------------
52
+ # DB helpers
53
+ # ----------------------------------------------------------------------
54
+
55
+ def _vec_to_blob(vec: list[float]) -> bytes:
56
+ """Pack a list of floats as little-endian f32 bytes for sqlite-vec."""
57
+ return struct.pack(f"<{len(vec)}f", *vec)
58
+
59
+
60
+ def _blob_to_vec(blob: bytes) -> list[float]:
61
+ n = len(blob) // 4
62
+ return list(struct.unpack(f"<{n}f", blob))
63
+
64
+
65
+ def _cosine(a: list[float], b: list[float]) -> float:
66
+ import math
67
+ dot = sum(x * y for x, y in zip(a, b))
68
+ na = math.sqrt(sum(x * x for x in a))
69
+ nb = math.sqrt(sum(y * y for y in b))
70
+ if na == 0 or nb == 0:
71
+ return 0.0
72
+ return dot / (na * nb)
73
+
74
+
75
+ def _get_db() -> sqlite3.Connection:
76
+ """Open DB and ensure schema. We use plain BLOB columns rather than
77
+ the sqlite-vec virtual table because sqlite-vec is an optional ext
78
+ that may not be loadable in every container — plain BLOB lets us
79
+ fall back to a Python-side cosine pass without losing correctness.
80
+ """
81
+ Path(DB_PATH).parent.mkdir(parents=True, exist_ok=True)
82
+ conn = sqlite3.connect(DB_PATH, timeout=10)
83
+ conn.execute("PRAGMA journal_mode=WAL")
84
+ conn.execute("""
85
+ CREATE TABLE IF NOT EXISTS chunks (
86
+ id TEXT PRIMARY KEY,
87
+ text TEXT,
88
+ embedding BLOB,
89
+ indexed_at REAL
90
+ )
91
+ """)
92
+ return conn
93
+
94
+
95
+ # ----------------------------------------------------------------------
96
+ # Embedding client
97
+ # ----------------------------------------------------------------------
98
+
99
+ _http: httpx.AsyncClient | None = None
100
+
101
+
102
+ def _client() -> httpx.AsyncClient:
103
+ global _http
104
+ if _http is None:
105
+ _http = httpx.AsyncClient(timeout=120.0)
106
+ return _http
107
+
108
+
109
+ async def _embed_batch(texts: list[str]) -> list[list[float]]:
110
+ """Embed a batch of texts.
111
+
112
+ Tries OpenAI-compatible shape first (POST <url>, Bearer auth,
113
+ response data[i].embedding). On failure, falls back to the
114
+ Pentatonic-AI gateway's native shape (POST .../v1/embed, X-API-Key
115
+ auth, response embeddings[i]). When the gateway eventually adds an
116
+ OpenAI-compat /v1/embeddings alias, the primary path will succeed
117
+ and the fallback will never fire — no code change needed.
118
+ """
119
+ if not texts:
120
+ return []
121
+ payload = {"input": texts, "model": EMBED_MODEL_NAME}
122
+ # Primary: OpenAI-compat
123
+ try:
124
+ resp = await _client().post(
125
+ NV_EMBED_URL,
126
+ headers=_openai_headers(),
127
+ json=payload,
128
+ timeout=120.0,
129
+ )
130
+ resp.raise_for_status()
131
+ return [d["embedding"] for d in resp.json()["data"]]
132
+ except Exception:
133
+ pass
134
+ # Fallback: lambda-gateway native shape
135
+ fallback_url = NV_EMBED_URL.replace("/v1/embeddings", "/v1/embed").replace("/embeddings", "/embed")
136
+ resp = await _client().post(
137
+ fallback_url,
138
+ headers=_lambda_headers(),
139
+ json=payload,
140
+ timeout=120.0,
141
+ )
142
+ resp.raise_for_status()
143
+ return resp.json()["embeddings"]
144
+
145
+
146
+ def _openai_headers() -> dict:
147
+ return {"Authorization": f"Bearer {EMBED_API_KEY}"} if EMBED_API_KEY else {}
148
+
149
+
150
+ def _lambda_headers() -> dict:
151
+ return {"X-API-Key": EMBED_API_KEY} if EMBED_API_KEY else {}
152
+
153
+
154
+ # ----------------------------------------------------------------------
155
+ # FastAPI
156
+ # ----------------------------------------------------------------------
157
+
158
+ class SearchRequest(BaseModel):
159
+ query: str
160
+ limit: int = 10
161
+
162
+
163
+ class IndexBatchRequest(BaseModel):
164
+ records: list[dict[str, Any]]
165
+
166
+
167
+ app = FastAPI(title="L4 sqlite-vec sidecar (OSS)")
168
+
169
+
170
+ @app.get("/health")
171
+ def health():
172
+ try:
173
+ conn = _get_db()
174
+ n = conn.execute("SELECT COUNT(*) FROM chunks").fetchone()[0]
175
+ conn.close()
176
+ return {"status": "ok", "loaded": True, "n_vectors": n,
177
+ "dim": EMBED_DIM, "db_path": DB_PATH, "backend": "sqlite-vec-fallback"}
178
+ except Exception as exc:
179
+ return {"status": "degraded", "error": str(exc)}
180
+
181
+
182
+ @app.post("/search")
183
+ async def search(req: SearchRequest):
184
+ if not req.query:
185
+ return []
186
+ try:
187
+ embs = await _embed_batch([req.query])
188
+ if not embs or embs[0] is None:
189
+ raise HTTPException(status_code=502, detail="embed failed")
190
+ q_vec = embs[0]
191
+ except Exception as exc:
192
+ raise HTTPException(status_code=502, detail=f"embed: {exc}")
193
+
194
+ conn = _get_db()
195
+ rows = conn.execute("SELECT id, text, embedding FROM chunks").fetchall()
196
+ conn.close()
197
+
198
+ # Cosine similarity in Python — fine for OSS / small corpora. For
199
+ # large corpora: consider a dedicated vector DB.
200
+ scored: list[tuple[float, str, str]] = []
201
+ for rid, text, blob in rows:
202
+ if not blob:
203
+ continue
204
+ v = _blob_to_vec(blob)
205
+ if len(v) != len(q_vec):
206
+ continue
207
+ s = _cosine(q_vec, v)
208
+ scored.append((s, rid, text))
209
+ scored.sort(reverse=True)
210
+ out = [
211
+ {"path": rid, "text": text, "score": float(s),
212
+ "source": "L4-sqlite-vec", "layer": "L4"}
213
+ for s, rid, text in scored[: req.limit]
214
+ ]
215
+ return out
216
+
217
+
218
+ @app.post("/index-batch")
219
+ async def index_batch(req: IndexBatchRequest):
220
+ if not req.records:
221
+ return {"status": "ok", "inserted": 0}
222
+ texts = [(r.get("text") or r.get("content") or "")[:8192] for r in req.records]
223
+ t0 = time.perf_counter()
224
+ embs = await _embed_batch(texts)
225
+ embed_ms = (time.perf_counter() - t0) * 1000.0
226
+
227
+ conn = _get_db()
228
+ t1 = time.perf_counter()
229
+ rows = []
230
+ for r, emb, txt in zip(req.records, embs, texts):
231
+ if not emb:
232
+ continue
233
+ rid = r.get("id") or hashlib.sha1(txt.encode("utf-8")).hexdigest()[:32]
234
+ rows.append((rid, txt, _vec_to_blob(emb), time.time()))
235
+ if rows:
236
+ conn.executemany(
237
+ "INSERT OR REPLACE INTO chunks(id, text, embedding, indexed_at) "
238
+ "VALUES (?, ?, ?, ?)", rows,
239
+ )
240
+ conn.commit()
241
+ insert_ms = (time.perf_counter() - t1) * 1000.0
242
+ conn.close()
243
+ return {"status": "ok", "inserted": len(rows),
244
+ "embed_ms": round(embed_ms, 1), "insert_ms": round(insert_ms, 1)}
245
+
246
+
247
+ @app.post("/refresh")
248
+ def refresh():
249
+ """No-op for sqlite-vec — writes are immediate. Kept for API parity."""
250
+ return {"status": "ok", "noop": True}
251
+
252
+
253
+ # ----------------------------------------------------------------------
254
+ # Entrypoint
255
+ # ----------------------------------------------------------------------
256
+
257
+ if __name__ == "__main__":
258
+ parser = argparse.ArgumentParser()
259
+ parser.add_argument("--port", type=int, default=int(os.environ.get("PORT", "8042")))
260
+ parser.add_argument("--data-dir", default=None)
261
+ args = parser.parse_args()
262
+ if args.data_dir:
263
+ os.environ["L4_DB_PATH"] = str(Path(args.data_dir) / "vec.db")
264
+ import uvicorn
265
+ uvicorn.run("server:app", host="0.0.0.0", port=args.port, log_level="info")
@@ -0,0 +1,9 @@
1
+ FROM python:3.12-slim
2
+ WORKDIR /app
3
+ RUN pip install --no-cache-dir fastapi "uvicorn[standard]" httpx "pymilvus[milvus_lite]" "setuptools<70" pydantic
4
+ COPY l5-comms-layer.py /app/server.py
5
+ RUN mkdir -p /data
6
+ ENV L5_DB_PATH=/data/comms.db
7
+ EXPOSE 8034
8
+ ENV HOST=0.0.0.0
9
+ CMD ["python", "server.py", "serve", "--port", "8034"]