CryoThrust commented on issue #12102:
URL: https://github.com/apache/seatunnel/issues/12102#issuecomment-5551354781

   ## STIP-38: Phase-1 design for a Mem0-compatible sink
   
   This turns the approved MVP boundary into a reviewable design before 
connector code starts. It intentionally remains a **connector-local source -> 
memory sink**. It does not add an engine/core/SPI memory abstraction, a Mem0 
source, conversational ingestion, or a scope-delete operation.
   
   ### 1. Connector boundary and configuration contract
   
   Proposed module name: `connector-mem0`.
   
   The connector accepts a normal SeaTunnel row and sends one logical operation 
per row. The first version has a deliberately small configuration surface:
   
   | Option | Type | Meaning |
   | --- | --- | --- |
   | `api_base_url` | required string | Base URL of the self-hosted 
Mem0-compatible REST endpoint; validation rejects malformed URLs. |
   | `api_key` | optional secret/string | Credential, supplied through the 
connector's secret/config mechanism and never logged. Existing `headers`, 
connect/socket timeout, and proxy/TLS configuration remain the HTTP-base 
configuration surface. |
   | `operation` | enum, default `UPSERT` | Static default: `UPSERT` or 
`DELETE`. |
   | `operation_field` | optional field name | When set, a row value may 
override the default with `UPSERT` or `DELETE`; unknown/null values fail that 
row rather than falling back silently. |
   | `memory_field` | field name | Required for every possible UPSERT row; 
string content to add/update. |
   | `memory_id_field` | field name | Required for every possible DELETE row; 
identifies exactly one backend memory. |
   | `user_id_field` | field name | Required scope for UPSERT. |
   | `agent_id_field`, `run_id_field` | optional field names | Optional Mem0 
scope dimensions for UPSERT. |
   | `metadata_field` | optional field name | JSON object copied as user 
metadata for UPSERT. |
   | `request_batch_size` | integer, default 1 | Batches transport only after 
the backend endpoint and per-record acknowledgement semantics are confirmed. It 
is not an acknowledgement shortcut. |
   
   Field-name options refer to the resolved SeaTunnel row schema, not an 
implicit JSONPath language. This keeps schema validation deterministic and 
avoids a second selector syntax in phase 1.
   
   Startup validation rejects:
   
   1. unsupported static `operation`;
   2. missing `memory_field` when an UPSERT is possible;
   3. missing `memory_id_field` when a DELETE is possible;
   4. missing `user_id_field` when an UPSERT is possible;
   5. duplicate/empty field selectors and selectors absent from the resolved 
input schema;
   6. a metadata field whose declared type cannot represent an object.
   
   Per-row validation rejects an invalid dynamic operation, non-string/empty 
UPSERT content, absent/empty required scope, an absent/empty DELETE memory id, 
or metadata that is not an object. Phase 1 has **no inferred scope deletion**: 
null content, null id, or a CDC DELETE must never turn into deletion of all 
memories for a user/agent/run.
   
   ### 2. Request mapping
   
   The exact wire adapter is isolated behind a small Mem0 client because 
self-hosted versions differ. The following logical mapping is stable:
   
   ```text
   UPSERT row
     memory_field   -> request memory/content
     user_id_field  -> request user_id
     agent_id_field -> request agent_id (when present)
     run_id_field   -> request run_id (when present)
     metadata_field -> request metadata (when present)
   
   DELETE row
     memory_id_field -> request memory identifier
   ```
   
   The connector owns only its reserved transport headers; it must not 
overwrite user metadata. Response bodies, tokens, and credential-bearing 
request details are never included in exception text or logs.
   
   ### 3. Delivery and retry contract
   
   The initial delivery guarantee is **at-least-once per row**. It must not 
claim exactly-once: a network failure after backend acceptance is 
indistinguishable from a failure before acceptance unless the selected backend 
exposes a durable idempotency mechanism.
   
   A future backend-specific idempotency header can be enabled only after its 
server-side semantics are documented and covered by integration tests. A 
locally derived hash alone is not a backend guarantee.
   
   Each logical row has an individual outcome:
   
   | Outcome | Action |
   | --- | --- |
   | Connect/read timeout, connection reset, HTTP 408/429, 5xx | Retry subject 
to the configured policy; preserve the original row identity in error 
reporting. |
   | HTTP 401/403, schema/request validation failure, other non-retryable 4xx | 
Do not retry; fail the row/job through the normal connector error path with a 
sanitized diagnostic. |
   | Successful acknowledgement for exactly that row | Mark only that row 
successful. |
   | Batch response with mixed outcomes | Acknowledge successful rows only; 
retry/surface unsuccessful rows individually. A whole batch must never be 
acknowledged because one item succeeded. |
   | Ambiguous response or unknown response shape | Treat as unsuccessful and 
surface a sanitized protocol error; do not infer success. |
   
   Ordering is preserved only inside the connector's single writer sequence. No 
cross-batch ordering or exactly-once claim is made in Phase 1.
   
   ### 4. HTTP-base integration assessment
   
   The implementation will reuse `HttpParameter` / `HttpCommonOptions` for 
headers, timeouts and I/O retry settings, and `HttpClientProvider` for client 
lifecycle where its APIs fit.
   
   However, the current generic `HttpSinkWriter` is not sufficient as-is: it 
logs and swallows non-200 responses, and `HttpClientProvider` retries I/O 
exceptions rather than classifying response status. That behavior cannot 
enforce the contract above. The implementation proposal will therefore either:
   
   1. add a narrowly scoped HTTP-base extension that exposes status/body 
handling and preserves headers for all needed verbs, with existing connector 
regression coverage; or
   2. use a connector-owned Mem0 client while reusing the existing common HTTP 
parameter/configuration path.
   
   The choice will be made in the implementation PR after a small compatibility 
spike. It will not change engine/core/SPI contracts.
   
   ### 5. Test matrix and acceptance gates
   
   **Unit tests**
   - all startup invalid combinations and resolved-schema errors;
   - UPSERT/DELETE serialization and optional scope/metadata mapping;
   - dynamic-operation validation;
   - response status classification and sanitization;
   - retryability classification;
   - no accidental scope deletion.
   
   **Deterministic integration tests**
   - local mock server verifies paths, headers, redaction, request order and 
body;
   - 200/201 success, 400/401/403 terminal failures, 408/429/5xx retry behavior;
   - connection failure and ambiguous post-write failure;
   - mixed per-record batch outcomes;
   - duplicate delivery after an ambiguous failure, proving the documented 
at-least-once behavior.
   
   **E2E decision**
   - a Mem0 Testcontainers profile is optional, not a prerequisite. It is added 
only if an official image can start deterministically within CI limits. The 
mock-server suite is the required acceptance gate.
   
   **Docs/examples**
   - JDBC -> Mem0 batch hydration;
   - MySQL-CDC -> Mem0 UPSERT/DELETE;
   - explicit delivery guarantee, required scopes, retry behavior, redaction, 
and Phase-1 non-goals.
   
   ### 6. Compatibility and implementation sequence
   
   1. Maintainer review of this contract.
   2. A small implementation PR for the connector, tests, examples and docs 
only.
   3. Only after a second memory backend is real and reviewed, reconsider a 
shared abstraction or a memory source/export path.
   
   Please confirm whether the field-name contract and the at-least-once/per-row 
failure boundary are acceptable. In particular, I would like confirmation that 
Phase 1 should keep DELETE limited to a single `memory_id`, rather than 
introducing scope deletion.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to