Maturity and install baseline
These tricks apply to the Preview Pydantic AI Agent adapter, the source-only LangGraph adapter, and the source-only LangChain middleware.
Pin source installs for reproducibility. A mutable branch install is Not validated as a reproducible release input. Real external-provider round trips are also Not validated here; start with model-free adapter checks where available.
Minimal test harness
Use the same query and a fixed project Scope in every framework:PowerContextClient; it must not start an embedded Server.
Execution flow under test
1. Scope: do not let cwd choose the tenant
Scope precedence is not uniform:
Pydantic AI hashes an explicit overlength Scope into a bounded ID. LangGraph and LangChain have no local fallback. Their missing-Scope error sits inside automatic fail-open handling, but explicit tools still report failure.
Wrong approach
Let a long-running service derive Scope from its process cwd, or return one constant Scope from every callback.Observation
Write one Memory for tenant A, then issue the same query for tenant B. If B recalls A’s entry, the adapter has crossed a data partition even though authentication may be correct.Repair
Map an authenticated and authorized tenant/project identity per run:Assertion
2. Tokens and transport: keep credentials out of model-visible data
All three adapters take a bare token. The Client constructs the Authorization header.Wrong approach
StoreBearer opaque-token in graph state, message metadata, a URL, or a model-visible tool argument. Another common mistake is sending a token over non-loopback plaintext HTTP.
Observation
State/checkpoint inspection can reveal the credential, and the Pydantic AI adapter rejects a complete Authorization value. LangGraph and LangChain document bare-token syntax but do not strictly validate it at the adapter boundary. Separately,PowerContextClient refuses non-loopback HTTP unless a caller-provided transport is explicitly trusted.
Repair
Keep the bare value in deployment secret management or Client configuration. Use HTTPS off loopback.repr=False and SecretStr reduce routine display; neither is a secret vault.
Assertion
http://powercontext.internal:8000 URL is rejected before any request is sent.
3. Untrusted evidence: old text cannot authorize an action
Wrong approach
Inject recalled content as an authoritative system instruction. Seed a Memory with:Observation
The model may treat an old user or model statement as current policy. A Citation proves origin and version; it does not prove present authorization.Repair
Use the adapter’s trust envelope and ephemeral channel:- Pydantic AI prepends one current-run system request and replaces its prior marker block on a new run;
- LangGraph writes
llm_input_messages; - LangChain overrides the current
ModelRequest.system_message.
Assertion
4. History pollution: recall belongs to the request, not storage
Wrong approach
Append one recalled system message to durablemessages on every turn.
Observation
After three turns, old blocks accumulate. With a checkpointer, they become persistent state; some providers also reject the resulting non-consecutive system messages.Repair
Use the integration’s temporary path and rebuild model input from clean history. Pydantic AI removes its recognized old marker before dispatch, LangGraph uses a last-valuellm_input_messages channel, and LangChain changes only the current request.
Assertion
all_messages(). The adapter can replace its marker before the next dispatch, but it cannot rewrite the application’s database policy.
5. Fresh preparation: follow the framework lifecycle
Wrong approach
Promise that every adapter prepares on every model step, or reuse one prepared block across separate runs.Observation
LangGraph intentionally returns the same prepared value inside one human-turn tool loop. Pydantic AI prepares again only when a new run begins. LangChain prepares for every model request.Repair
Key freshness to the host lifecycle. A new run or human turn must prepare again at the frequency in the table. Never use a prior turn’s block merely because old conversation history was reused.Assertion
6. UTF-8 budgets: query characters and context bytes are separate
The common query maximum is 8192 characters. PreparedContextmax_bytes is a UTF-8 byte budget from 512 through 32768; the adapter default is 8000. LangChain completed-turn capture has another bound: 200,000 characters including its framing.
Wrong approach
Uselen(content) as the PreparedContext size check, then assume a 9000-character Chinese string exercises only one limit.
Observation
A Chinese character usually occupies more than one UTF-8 byte. Query clamping and context truncation happen at separate boundaries; LangChain’s capture bound is character-based again.Repair
Test each contract independently. Send 9000 multi-byte characters to exercise query clamping, request a smallmax_bytes to exercise PreparedContext truncation, and test LangChain capture framing near its 200,000-character limit.
Assertion
7. Capture without flush: Source is not Memory
Wrong approach
Enable capture, run one turn, and immediately assert that Memory search returns the captured text.Observation
Capture may have succeeded while search remains empty. LangChain stores a completed-turn Source but does not flush it. LangGraph has no capture path. Pydantic AI can attempt checkpoint/final flush when capture is enabled, but extraction can still no-op.Repair
Keep the capture receipt and position. Confirm the processing cursor reaches that position, then inspect Memory entrysource_refs. Search or prepare comes last.
Assertion
8. Failure semantics: fail open only on automatic helpers
Wrong approach
Turn every Server error into empty Memory, or swallow an explicit mutation failure because recall is best effort.Observation
The model cannot tell “no matching Memory” from authentication failure or outage. Worse, a failed write may appear successful.Repair
Keep automatic and explicit paths separate:
A 401/403 diagnostic must not include token, query, Scope content, or response body.
Assertion
9. Failure and privacy experiments
Server offline
Stop the Server and repeat the host request. The original task continues. Then call an explicit remember tool and confirm that the model sees failure.Wrong token
Use an invalid token. A 401 or 403 must not become “no Memory,” and diagnostics must remain content-free.Same query, different Scope
Seed Scope A but not B. Run the same query in both and assert that B receives nothing from A.History pollution
Enable a checkpointer or durable message store. Run several turns and inspect persisted history for recalled blocks.Capture privacy
Before opting in, send credential-like text and ordinary business-sensitive text. Pydantic AI redacts known credential shapes, not every sensitive business value. LangChain does not promise complete redaction.Currently unsupported or not validated
Completion checklist
- Scope is explicit per tenant/run and has a cross-Scope negative test.
- Bare tokens stay in Client/deployment configuration and off model-visible channels.
- Non-loopback traffic uses HTTPS unless transport security is explicitly trusted.
- Recalled evidence is labeled untrusted and remains ephemeral.
- Prepare call counts match each framework’s lifecycle.
- Query characters, PreparedContext UTF-8 bytes, and LangChain capture characters are tested separately.
- Capture receipts, cursor progress, and Memory lineage are checked in order.
- Automatic fail-open and explicit failure remain distinct.
- Unsupported and Not validated claims are recorded separately.
A release is incomplete if its only assertion is that the model answered. Inspect Scope, request input, persisted history, Source position, cursor, lineage, and failure diagnostics.

