OpenHands / OpenHands/software-agent-sdk

SDK outsources the capabilities of models it vouches for to a third party's main branch

Open
#4,880 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement llm ready-for-dev security-related
Dominant language
Python
Stars
1.1k
Forks
539
Avg merge
1d 19h
Merged PRs (30d)
137

Description

Desired Behavior

The capabilities of the models we tell users we support should be ours: deterministic, reviewed, and changing only when we decide. Today they are not.

litellm/__init__.py fetches model_prices_and_context_window.json from the tip of BerriAI/litellm@main at import time and stores it in a module global. Pinning litellm in uv.lock pins its code, not that data. The SDK overrides this nowhere, in tests or in production:

$ grep -rn "LITELLM_LOCAL_MODEL_COST_MAP\|LITELLM_MODEL_COST_MAP_URL" openhands-sdk/ openhands-agent-server/
(no matches)

So context windows and capability flags — which decide when we truncate and what we put on the wire — arrive live from a third party's branch. That file took ≥100 commits in the last 7 days, 10 in the last 24 hours. #4877 was this going wrong in the mildest possible way: no code changed, upstream did, and sdk-tests went red on main and every open branch.

The inconsistency, in one line: we curate which models we support but outsource what those models can do. The SDK already declares VERIFIED_MODELS — 125 models across 11 providers — and already hand-maintains ~37 per-model facts across seven ad-hoc lists (PROMPT_CACHE_MODELS, EXTENDED_THINKING_MODELS, SEND_REASONING_CONTENT_MODELS, FORCE_STRING_SERIALIZER_MODELS, REQUIRES_INLINE_IMAGE_DATA_MODELS, REASONING_EFFORT_MODEL_OVERRIDES, VISION_MODEL_OVERRIDES). Adding a model is already a reviewed code change — #4861 was exactly that. But the context window of a model we ship support for still comes from someone else's main.

Proposed fix: own the behavioural metadata for the models we vouch for

One owned table keyed by model, consulted before LiteLLM, covering only the fields that change behaviour:

field class examples owner why
Behavioural context window, supports_vision, supports_prompt_cache, supports_reasoning_effort us, for VERIFIED_MODELS decides truncation and what goes on the wire; wrong here breaks a request
Accounting input/output price, cache costs upstream, live decides reporting only; a day stale is harmless

Roughly six fields for ~125 models, consolidating the seven lists above. Anything outside the verified set falls through to live LiteLLM data and is labelled unverified rather than silently treated as equivalent.

What that buys:

  • Supported models behave identically on every build, and change only through a reviewed diff.
  • New models still work the day upstream adds them, via the fallback — a user bringing their own model wants freshness, and we have made no promise about it.
  • CI asserts our values, so #4879's "never assert a value the upstream database owns" rule becomes unnecessary rather than a standing tax, and test_kimi_k3_supports_vision stops being a live landmine.
  • Upstream drift becomes a signal to review rather than an outage.

What it does not fix, stated plainly: the fallback path keeps the exposure for unverified models. That is the intended trade — bounded blast radius (limits, capabilities, cost reporting; no code execution and no traffic redirection, verified across all 3818 entries: the schema carries no api_base, api_key, URL or endpoint field), and freshness is what a user configuring an unlisted model actually wants.

Rejected alternatives, with the evidence
  • Pin the map to an immutable commit — built in #4881, closed. Freezing the whole map makes models added upstream afterwards invisible: an 8-day-old pin stripped vision and the context window from gpt-6-astra, which this repo shipped support for in #4861 the day before. Pinning our belief does not pin the provider APIs it describes.
  • Vendor the JSON in this repo — the same freeze with a different transport, so the same defect. At 10 commits/day into a 2.3 MB file the review is also nominal: refresh often and it approximates the live fetch with added latency; refresh rarely and it approximates #4881.
  • Use the copy bundled in the litellm wheel (LITELLM_LOCAL_MODEL_COST_MAP=True) — that snapshot lags even at release. Today's v1.100.0 ships 3408 models against main's 3817, and lacks gpt-6-astra. On our current pin (1.93.0, published 2026-07-19) it is 2954 models and lacks kimi-k3, which breaks 11 existing tests.
  • Ask upstream to sign the artifact — raised as BerriAI/litellm#40051 and closed by us. For the record on what verification exists today: none. LiteLLM checks that the payload is a non-empty dict and that the model count has not shrunk past a floor/ratio versus the bundled copy — a corruption check, not a tamper check; grep -niE "sha256|hashlib|signature|verify|attest" over its loader returns nothing, and anything preserving the model count passes. But signing would not have closed it either: transit is already TLS-verified, and the dominant threat (a compromised merge, account or CI — the shape of BerriAI/litellm#24518) yields validly signed data. The structurally useful upstream change is instead to regenerate the bundled snapshot at release and make the import-time fetch opt-in; noted there, not pursued until our own pin is current.
Acceptance Criteria
  • Decide whether owning behavioural metadata for VERIFIED_MODELS is the direction, or record why not. This is a design change to how the SDK resolves capability, so it wants a maintainer's yes before code.
  • Upgrade litellm. The pin is 1.93.0 (2026-07-19) against 1.100.0 — seven weeks and seven minor versions, on a dependency that has been compromised once (BerriAI/litellm#24518). This does not improve determinism on its own, but it removes a confound from every measurement above and is a prerequisite for any bundled-map variant.
  • Add the owned table for the behavioural fields, consolidating the seven ad-hoc lists, with the verified set as its key.
  • Fall back to live LiteLLM data for unverified models, and surface that a model is unverified rather than hiding it.
  • Detect divergence between our declared values and upstream's, so a provider change becomes a review prompt instead of silent drift.
  • Land provenance so the source actually in use is recorded (#4882 — diagnostics only; it makes the data neither trustworthy nor deterministic).
Notes

Found while root-causing #4877. Every measurement above was taken against litellm 1.93.0 on 2026-09-06–07; the pinning attempt and its failure are in #4881, and the upstream investigation in BerriAI/litellm#40051. Related: #4308, which catalogues this class of finding but only for build-time installs; a runtime data dependency is a category it does not cover, which is how this went unnoticed.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the import-time model data loading in litellm/init.py, then review VERIFIED_MODELS and the seven capability lists named in the issue. Check the existing sdk-tests, including test_kimi_k3_supports_vision, and the current litellm pin in uv.lock. Done means a maintainer-approved ownership model, consolidated verified metadata, an unverified fallback, divergence detection, and provenance diagnostics.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
ai, backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.