feat: project_id obbligatorio (gateway v2.4.0 + estensione v1.4.0)
- gateway: MemoryIn.project_id required (min_length=1) → POST senza project_id = 422 - estensione: qmem_store project_id Type.String (non più Optional), descrizione con obbligo e rinvio a qmem_meta; qmem_correct resta opzionale (eredita dal superseduto) - README/playbook sez. 11.5 aggiornati
This commit is contained in:
@@ -19,7 +19,7 @@ Oppure copia `extensions/index.ts` in `~/.pi/agent/extensions/pi-qmem/`.
|
||||
|
||||
| Tool | Descrizione |
|
||||
|---|---|
|
||||
| `qmem_store` | Salva un record di memoria (text, kind, agent_id, scope, project_id, source, expires_at, supersedes_id, supersede_reason) |
|
||||
| `qmem_store` | Salva un record di memoria (text, kind, agent_id, **project_id obbligatorio**, scope, source, expires_at, supersedes_id, supersede_reason) |
|
||||
| `qmem_search` | Ricerca semantica su tutta la conoscenza condivisa (query, kind, project_id, scope, top_k, include_superseded, min_score) |
|
||||
| `qmem_correct` | Corregge una memoria falsa: crea un nuovo record che **supersede** il vecchio (che resta in archivio marcato superseded) |
|
||||
| `qmem_meta` | Discovery: panoramica di scope×kind, progetti, agenti e superseduti (per scegliere i filtri di ricerca) |
|
||||
|
||||
@@ -348,6 +348,14 @@ pi install git:git.enne2.net/enne2/<repo>
|
||||
- Tool estensione: `qmem_meta` (nessun parametro) → guida la ricerca settorializzata (`scope`/`kind`/`project_id`)
|
||||
- Note: scope/kind sono enum chiusi (la discovery serve per conteggi e set aperti project_id/agent_id); espone i nomi reali di progetti/agenti (accesso condiviso già scelto)
|
||||
|
||||
### 11.5 project_id obbligatorio
|
||||
|
||||
- Dal gateway v2.4.0 / estensione v1.4.0: `project_id` è **obbligatorio** in `POST /v1/memories` (Pydantic `min_length=1`) e nello schema del tool `qmem_store` (Type.String, non più Optional)
|
||||
- POST senza `project_id` → **422** (validazione Pydantic); il tool rifiuta la chiamata senza project_id
|
||||
- `qmem_correct` mantiene `project_id` opzionale: lo **eredita** dal record superseduto (GET/search) — il gateway valida comunque il risultato
|
||||
- L'agente consulta `qmem_meta` per riusare gli id esistenti; domini nuovi → kebab-case
|
||||
- Regola nel prompt: AGENTS.md sez. "Obbligo di project_id"
|
||||
|
||||
### 11.3 Verifica rapida
|
||||
|
||||
```bash
|
||||
|
||||
+7
-2
@@ -124,7 +124,8 @@ export default function qmemExtension(pi: ExtensionAPI) {
|
||||
"Nessun LLM in scrittura: salva fatti, decisioni, preferenze o episodi deliberati e strutturati. " +
|
||||
"Usa kind=decision per scelte con motivazione, kind=fact per fatti stabili, kind=preference per " +
|
||||
"preferenze utente, kind=episode per esiti di azioni completate. Non salvare transcript grezzi: " +
|
||||
"salva un record compatto e ad alto segnale per evento significativo.",
|
||||
"salva un record compatto e ad alto segnale per evento significativo. " +
|
||||
"project_id è OBBLIGATORIO: consulta qmem_meta per i progetti esistenti e riusa l'id appropriato.",
|
||||
parameters: Type.Object({
|
||||
text: Type.String({ description: "Il contenuto del record di memoria (compatto, ad alto segnale)." }),
|
||||
kind: Type.Optional(
|
||||
@@ -134,7 +135,11 @@ export default function qmemExtension(pi: ExtensionAPI) {
|
||||
),
|
||||
),
|
||||
agent_id: Type.Optional(Type.String({ description: "Nome dell'agente che scrive (solo provenienza, nessun isolamento)." })),
|
||||
project_id: Type.Optional(Type.String({ description: "Progetto di appartenenza (organizzativo)." })),
|
||||
project_id: Type.String({
|
||||
description:
|
||||
"OBBLIGATORIO: progetto/dominio di appartenenza (kebab-case, es. pi-qmem, domotics, frigate-tts). " +
|
||||
"Consulta qmem_meta per i progetti esistenti e riusa l'id appropriato; per domini nuovi crea un id coerente.",
|
||||
}),
|
||||
scope: Type.Optional(
|
||||
Type.Union([Type.Literal("agent"), Type.Literal("project"), Type.Literal("org")], {
|
||||
description: "Scope organizzativo (default: agent).",
|
||||
|
||||
+2
-2
@@ -50,7 +50,7 @@ MAX_TEXT_LEN = int(os.environ.get("MAX_TEXT_LEN", "8000"))
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
|
||||
log = logging.getLogger("memory-gateway")
|
||||
|
||||
app = FastAPI(title="Memory Gateway", version="2.3.0")
|
||||
app = FastAPI(title="Memory Gateway", version="2.4.0")
|
||||
qdrant = QdrantClient(url=QDRANT_URL, api_key=QDRANT_API_KEY)
|
||||
|
||||
# Rate limit in-memory: {key: [timestamps]}
|
||||
@@ -72,7 +72,7 @@ class MemoryIn(BaseModel):
|
||||
text: str = Field(min_length=1, max_length=MAX_TEXT_LEN)
|
||||
kind: Literal["decision", "fact", "episode", "preference"] = "fact"
|
||||
agent_id: Optional[str] = Field(default=None, max_length=64, description="Solo provenienza, nessun isolamento")
|
||||
project_id: Optional[str] = Field(default=None, max_length=64)
|
||||
project_id: str = Field(min_length=1, max_length=64, description="OBBLIGATORIO: progetto/dominio di appartenenza (kebab-case)")
|
||||
scope: Literal["agent", "project", "org"] = "agent"
|
||||
source: Optional[str] = Field(default=None, max_length=256)
|
||||
expires_at: Optional[str] = None # ISO 8601
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pi-qmem",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.0",
|
||||
"description": "Memoria centralizzata e condivisa per agenti AI: salva e cerca record semantici (Qdrant + BGE-M3) via Memory Gateway.",
|
||||
"keywords": ["pi-package", "memory", "agent", "qdrant", "rag"],
|
||||
"license": "MIT",
|
||||
|
||||
Reference in New Issue
Block a user