Skip to content

Type SESSION_PROPOSE config with a validated SessionProposeConfig model (#18)#20

Open
phillipclapham wants to merge 1 commit into
sunilp:mainfrom
phillipclapham:typed-session-propose-config
Open

Type SESSION_PROPOSE config with a validated SessionProposeConfig model (#18)#20
phillipclapham wants to merge 1 commit into
sunilp:mainfrom
phillipclapham:typed-session-propose-config

Conversation

@phillipclapham

Copy link
Copy Markdown
Contributor

Closes #18. Rebased onto main now that #17 has landed, so the diff is just the typed-config change.

Replaces the config: dict[str, Any] hatch on LdpMessageBody with a typed SessionProposeConfig, so Pydantic enforces the wire shape at the boundary:

class SessionProposeConfig(BaseModel):
    preferred_payload_modes: list[PayloadMode] = Field(default_factory=list)
    ttl_secs: int | None = Field(default=None, gt=0)
    trust_domain: str  # required, non-blank
  • preferred_payload_modes keeps the forward-compat tolerance the manual guards from Mode 3 (SEMANTIC_GRAPH) negotiation via declared support + codec registration (#15) + lifecycle hooks (#14) #17 added — a mode="before" validator drops non-list / unknown / non-string entries, so a peer on a newer mode set degrades to the shared known modes instead of failing the handshake. Those manual guards are retired from the handler.
  • trust_domain is required and non-blank (validator rejects "" / whitespace, mirroring TrustDomain.name). Deliberate contract tightening: previously a SESSION_PROPOSE missing trust_domain (or with no config) was treated as the "unknown" domain; now it's rejected at the boundary, and a model_validator requires config for SESSION_PROPOSE so it holds end to end. The bundled client always sends trust_domain, so it's unaffected. If you'd rather keep the lenient default, trust_domain: str = "unknown" is a one-line change.
  • ttl_secs, when present, must be positive.

One design question: LdpMessageBody is a flat model, so the typed config field is validated on every message type, not just SESSION_PROPOSE — a future message carrying a config that isn't a valid SessionProposeConfig would be rejected at the boundary rather than reaching the unknown-message path. That's consistent with how the model already validates every other typed field (negotiated_mode, contract, …), and no current message carries config elsewhere, so there's no blast radius today. If you'd want config to stay a universal extension hatch, the clean options are a discriminated union or conditionally coercing config only when type == "SESSION_PROPOSE" — your call on evolvability, happy to implement either.

Heads-up for the Rust mirror: Python conveys SESSION_PROPOSE mode prefs as config.preferred_payload_modes, but the Rust server reads a top-level payload_mode (src/server.rs), so Python↔Rust mode negotiation looks like it diverges today (predates this PR). Worth a cross-SDK JSON fixture when you align Rust — happy to file a separate issue.

Tests cover: drops unknown modes, coerces strings, requires non-blank trust_domain, rejects non-positive ttl, requires config for SESSION_PROPOSE, validates through the envelope boundary, and the factory dict path enforces the same. Also fixed examples/session_flow.json, which used required_trust_domain (a client-config field name) in the wire config.

…el (sunilp#18)

Replace the config: dict[str, Any] hatch on LdpMessageBody with a typed
SessionProposeConfig so Pydantic enforces the wire shape at the boundary.
trust_domain is required; preferred_payload_modes drops unknown / malformed
entries via a field validator, preserving the forward-compat tolerance the
manual guards from sunilp#17 provided (now retired from the handler). The client
builds the typed model. Composes with sunilp#19: a malformed config now raises at
parse time and is mapped to a 400 by the route.
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@phillipclapham, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 1 hour. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6782db57-3ed8-4236-8a15-dc22ea66159f

📥 Commits

Reviewing files that changed from the base of the PR and between c59c1fe and 62f9d4f.

📒 Files selected for processing (7)
  • examples/session_flow.json
  • sdk/python/src/ldp_protocol/client.py
  • sdk/python/src/ldp_protocol/delegate.py
  • sdk/python/src/ldp_protocol/types/__init__.py
  • sdk/python/src/ldp_protocol/types/messages.py
  • sdk/python/tests/test_client.py
  • sdk/python/tests/test_types.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Typed SessionProposeConfig to replace the config dict[str, Any] hatch

1 participant