Repository navigation
camel-goodmem 0.2.0: official SDK, retrieval status contract, native retriever - #1
Merged
Merged
Conversation
…retriever Rewrites the package against the P1-P38 checklist. Every defect below was reproduced against the PUBLISHED wheel (camel-goodmem 0.1.0 on PyPI, verified byte-identical to b12a805) running live against GoodMem server v1.0.320. Reproduced and fixed: - P1 hand-rolled `requests` client -> official `goodmem` SDK - P2 `publicRead` was sent by goodmem_update_space, and the server answers `400 Unrecognized field "publicRead"`. The SDK's own UpdateSpaceRequest has no such field; the argument is gone - P3 a malformed NDJSON line was swallowed and the search reported success; now what arrived is kept and reported as MALFORMED_STREAM - P4 `status` events had no branch in the parser: a search with a broken reranker returned success=true with no status, while the server had sent NOT_FOUND, FEATURE_DISABLED and RERANKING_FAILED. Now the Q1/Q3/Q4a/Q4b contract, with `partial` + `statuses` - P5 `wait_for_indexing` defaulted on and was model-controllable: 11.63s on an empty space, 0.33s without. The read path no longer polls - P6 listings returned page one; the server's `nextToken` was never read - P7 the server's field-level errors were replaced by "400 Client Error" - P10 `file_path` was a model-facing argument with no restriction and read /etc/hostname byte-identical. Uploads now require an `upload_dir` - P12 a failed content fetch set `contentError` and left `success: true` - P16 a PDF's content came back as raw `bytes` -- not JSON-serialisable, so that tool result could never reach a model. Now text as text, anything else base64 - P15/P30 chunks and memories were two arrays joined by positional `memoryIndex`; now joined by UUID, de-duplicated by chunk id - P17 none of the 12 HTTP calls carried a timeout - P19/P34 `metadata_filter` was a raw expression string supplied by the MODEL. Live, the model widened its own scope (1 hit -> 2) and an ordinary apostrophe in a value was a 400. Filters are now developer-set and built by `camel_goodmem.filters`, with the escaping the server accepts and casts matching the stored type - P28 retrieval showed the model 13 arguments including `poll_interval` and `llm_temperature`; `delete_space` and `update_space` were always in the toolset. The model now sees `goodmem_search(query, top_k)` plus an optional write; admin, delete and upload are opt-in - P29 the threshold was documented "Minimum score (0-1)" and raw negative vector scores were passed through. Scores are oriented higher-is-better with rawScore/scoreKind kept, reranker scores are never negated, and a threshold is reranker-only and warns when it empties the result - P21/P36 the API key was a public attribute; it is now private and absent from repr - P32 reusing a space name silently accepted a different embedder. Reuse now requires a match; a mismatch is an error naming both - P35 there was no retriever, so GoodMem could not be used with CAMEL's RAG paths. Adds GoodMemRetriever(BaseRetriever) Tests: 71 pre-existing tests passed against every defect above; they assigned a MagicMock to the toolkit's `_session`. Replaced with 69 offline (the real SDK over a mock transport, fed NDJSON captured from v1.0.320) plus 29 live, which skip without credentials and verify teardown against a server inventory. CI added: this repo had only a publish workflow, so 0.1.0 shipped without any test ever running. CI now runs lint, types, the offline suite, the skip check, a clean-environment install, and gates for a committed key and disabled TLS. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
camel-ai's BaseToolkit does `from mcp.server import FastMCP`, which mcp 2.x moved, and camel-ai only requires `mcp>=1.3.0`. So `pip install camel-goodmem==0.1.0` into a clean environment today resolves mcp 2.2.0 and raises ImportError on `import camel_goodmem` -- the published package is unusable for any new user, independently of every other defect in this branch. Verified: a clean venv with 0.1.0 fails to import; the same venv with this branch resolves mcp 1.30.0 and imports. CI's clean-environment install step covers it from here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The shipped example still called the 0.1.0 method names (goodmem_list_embedders, goodmem_create_space), handed every tool to every agent and let the model pick the space, and asked the model to compose a raw CAST(val(...)) filter string -- the exact P28 and P34 shapes the rewrite removed. It also never deleted the three spaces it created. Now: one admin toolkit for setup and teardown, a toolkit scoped per space for each agent, scenario 3 uses a developer-set metadata_filter, and cleanup verifies against a fresh listing. Every call is checked against the real method surface. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Audit and rewrite of
camel-goodmemagainst the P1–P38 checklist.Everything below was reproduced against the published wheel —
camel-goodmem0.1.0 on PyPI, verified byte-identical tob12a805— running live against GoodMem server v1.0.320, not inferred from reading the source.The headline
goodmem_update_spacesentpublicRead. The server removed that field and answers:metadata_filterwas a raw filter expression supplied by the model. Live, a model could widen its own scope — the intendedtenant = 'acme'filter returned 1 hit, a self-widened one returned 2 — and an ordinary apostrophe in a value was a 400.A search with an unavailable reranker returned
success: truewith no indication of trouble, while the server had sentNOT_FOUND,FEATURE_DISABLEDandRERANKING_FAILED.A PDF's content came back as raw
bytes, which is not JSON-serialisable — that tool result could never reach a model.The 71 existing tests passed against every one of these. They assigned a
MagicMockto the toolkit's_session, so they asserted that the code calls the methods it calls. There was no CI: only a publish workflow, so 0.1.0 shipped without a test ever running.Measured before/after
update_space(public_read=True)Unrecognized field "publicRead"success=true, no statuspartial=true+ the server's own messagesbytes, not JSON-serialisablestr, byte-identicalfile_path=/etc/hostname..and symlink escapesdelete_spacealways exposedgoodmem_search(query, top_k)-0.6236score=+0.6236,rawScore,scoreKind400 Client Error: Bad RequestGoodMemRetriever(BaseRetriever)Full transcript:
camel-goodmem-before-after.txtin the audit notes.Tests
httpxmock transport, fed NDJSON captured from v1.0.320. A guard test asserts the fixtures are server bytes.Deliberately not done
AgentMemoryimplementation: CAMEL'sAgentMemoryis chat history with a context-window policy, GoodMem is a document store with server-side embedding. Implementing it would fake one side of the contract.GoodMemToolkitmoved fromcamel_goodmem.goodmem_toolkittocamel_goodmem.toolkit; the package-level importfrom camel_goodmem import GoodMemToolkitis unchanged.🤖 Generated with Claude Code