Skip to content

fix(sdk): decrypt encrypted fallback profiles - #4568

Draft
Sehlani042 wants to merge 1 commit into
OpenHands:mainfrom
Sehlani042:codex/fix-fallback-profile-cipher
Draft

fix(sdk): decrypt encrypted fallback profiles#4568
Sehlani042 wants to merge 1 commit into
OpenHands:mainfrom
Sehlani042:codex/fix-fallback-profile-cipher

Conversation

@Sehlani042

Copy link
Copy Markdown
Contributor

HUMAN:


AGENT:

Why

Encrypted LLM profiles are decrypted on the main conversation path, but fallback profiles are resolved lazily through a separate LLMProfileStore that did not receive the conversation cipher. As a result, FallbackStrategy could pass a Fernet ciphertext (gAAAAA...) to the provider as an API key. This also affected strategies that had cached a fallback before being attached to a conversation.

Summary

  • Allow LLMProfileStore to retain an optional default cipher while preserving explicit per-load cipher precedence.
  • Bind the conversation cipher to FallbackStrategy and invalidate previously resolved fallback/profile-store caches when the cipher changes.
  • Add a deterministic regression covering encrypted fallback resolution before and after conversation binding.

Issue Number

Fixes #4558

How to Test

Reproduction and regression:

  1. Save a fallback LLM profile with include_secrets=True and a Cipher.
  2. Resolve it through an unbound FallbackStrategy; the old path exposes the encrypted gAAAAA... token.
  3. Create LocalConversation(..., cipher=cipher) with that strategy.
  4. Resolve the fallback again and verify the API key is the original plaintext value.

Commands and results:

uv run pytest -q \
  tests/sdk/llm/test_llm_fallback.py \
  tests/sdk/llm/test_llm_profile_store.py \
  tests/sdk/conversation/test_switch_model.py
# 110 passed
uv run pre-commit run --files \
  openhands-sdk/openhands/sdk/conversation/impl/local_conversation.py \
  openhands-sdk/openhands/sdk/llm/fallback_strategy.py \
  openhands-sdk/openhands/sdk/llm/llm_profile_store.py \
  tests/sdk/llm/test_llm_fallback.py
# Ruff format/lint, PEP8, Pyright, import rules, and Tool registration passed
uv run pytest -q tests/sdk
# 5948 passed, 9 skipped, 10 xfailed, 2 failed

The same two full-suite failures reproduce on untouched origin/main: LiteLLM now reports support for moonshot/kimi-k3, and the local nonexistent MCP endpoint returns HTTP 503. No additional failure is introduced by this patch.

git diff --check also passes.

Video/Screenshots

Not applicable; this is an SDK behavior fix covered by a deterministic executable regression.

Design Doc

Not included; the change is limited to cipher propagation and cache invalidation across existing SDK objects.

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

Codex assisted with issue triage, implementation, and test execution. The patch was constrained to the fallback-profile cipher path and checked for competing pull requests before publication.

Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions

Copy link
Copy Markdown
Contributor

📁 PR Artifacts Notice

This PR contains a .pr/ directory with temporary PR-specific documents. Because this is a fork PR, the directory will be automatically removed from main immediately after merge.

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.

Encrypted LLM profiles are decrypted only on the conversation path — FallbackStrategy (and sub-agents) load them without the cipher

1 participant