Skip to main content
Framework integration bugs are often quiet: two tenants share a Scope, recalled blocks leak into history, a character count is mistaken for a byte budget, or capture is treated as a Memory write. Test each boundary the same way: wrong approach → observation → repair → assertion.

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:
Run the Server separately. The adapter application should connect through PowerContextClient; it must not start an embedded Server.

Execution flow under test

The wrong approach → observation → repair → assertion sections below test one boundary at a time.

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

Scope selects data. It does not authenticate the caller or grant tool permission.

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

Store Bearer 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

Also test that an untrusted 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.
Keep current system/user instructions and the host permission gate in control. Recalled text grants no permission.

Assertion

4. History pollution: recall belongs to the request, not storage

Wrong approach

Append one recalled system message to durable messages 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-value llm_input_messages channel, and LangChain changes only the current request.

Assertion

An application may choose to store complete Pydantic AI 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. PreparedContext max_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

Use len(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 small max_bytes to exercise PreparedContext truncation, and test LangChain capture framing near its 200,000-character limit.

Assertion

Do not compare the 8192-character query limit with the 32768-byte context ceiling as if they measured the same value.

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 entry source_refs. Search or prepare comes last.

Assertion

If the cursor advances but no entry appears, report extraction no-op instead of inventing a Memory write.

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.
Framework Integration ends here. Continue with Common agents or Coding agents.