Skip to content

[multi-team P3d] Scope LLM lookups and context-window limits to the window's team - #15561

Open
warp-agent-staging[bot] wants to merge 12 commits into
masterfrom
factory/multi-team-pr3b-scoped-llm-lookups
Open

[multi-team P3d] Scope LLM lookups and context-window limits to the window's team#15561
warp-agent-staging[bot] wants to merge 12 commits into
masterfrom
factory/multi-team-pr3b-scoped-llm-lookups

Conversation

@warp-agent-staging

@warp-agent-staging warp-agent-staging Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Description

Stacked on #15567 (PR 3), which is stacked on #15565 (PR 2) and #15463 (PR 1). PR 1 partitions the model catalog by team (as Team.feature_model_choice / Workspace.feature_model_choice, read through UserWorkspaces) but leaves LLMInfo id-lookups and context-window limits resolved against either the unscoped, search-every-team get_llm_info, or an inherited_or_default_team_uid-resolved fallback, for callers that don't yet thread a live TeamScope through. This PR closes that gap: every caller that already has a live TeamScope in hand now resolves policy-affecting LLMInfo metadata and context-window limits against that exact team, not an arbitrary or inherited one.

get_llm_info_for_scope / get_llm_info_for_team_uid

LLMPreferences::get_llm_info resolves an id by scanning every team's bucket and returning the first match, so a model with team-specific disable_reason/host_configs could resolve to the wrong team's metadata. Adds get_llm_info_for_team_uid (resolves against one team's own catalog) and get_llm_info_for_scope (the TeamScope-taking convenience wrapper), and migrates every policy- or consistency-affecting call site that already has a scope in hand:

  • The model picker (profile_model_selector.rs's refresh_model_menu and get_selected_llm_info).
  • The AWS Bedrock login banner decision (terminal/view.rs's maybe_insert_aws_bedrock_login_banner, which reads host_configs for this terminal's own team — a team that disables Bedrock must not show the banner because another team's model happens to support it, and vice versa).
  • Request-time Grok/GEAP routing checks (response_stream.rs's spawn_request), via a new RequestParams::team_uid field so the check reads the same team the request was built for.
  • The execution-profile settings page's display rows (settings_view/execution_profile_view.rs), scoped for consistency with the team_uid already resolved alongside them.

The unscoped get_llm_info stays in execution_profiles/profiles.rs (a model-edit telemetry call site with no window to mint a scope from) and anywhere else display-only metadata is read with no scope in hand.

Context-window helpers now use the caller's live scope

effective_base_model and the whole AIExecutionProfileAppExt trait (configurable_context_window, context_window_display_value, context_window_limit_for_request, should_show_long_context_pricing_warning) previously resolved the base model via an inherited/default team scope rather than the caller's actual one. On a window viewing a non-default team, this could show/hide the context-window control using another team's model metadata, and — more seriously — clamp the persisted context-window override (context_window_limit_for_request, used by RequestParams::new) against another team's [min, max] before sending the actual request.

Threads &impl TeamScope through effective_base_model and the trait, and passes the already-resolved scope from every caller that has one: ExecutionProfileEditorView, settings_view/agent_profiles_page.rs's context-window call sites, and RequestParams::new (ai/agent/api.rs, via the new team_uid field piggybacked onto the same scope read).

Tests

  • workspaces::update_manager::tests::context_window_limit_for_request_clamps_against_the_scoped_teams_own_range — two teams share a model id but advertise different configurable ranges; the same requested limit clamps differently per team, and a caller passing team A's scope never sees team B's clamp (or vice versa).
  • workspaces::update_manager::tests::on_workspaces_updated_keeps_teams_distinct_and_prunes_a_team_the_response_omits (carried over from PR 1) asserts through get_llm_info_for_team_uid directly instead of the coarser choices-iterator check PR 1 used as a placeholder, since the precise accessor exists as of this PR.

Note on re-targeting

This PR replaces #15553 and has now been rebased onto #15567, the current third PR in the catalog-placement stack. Its diff is again only the scoped lookup and context-window work: 14 files, +311/-84.

Visual proof

Not attempted here. Demonstrating two teams with genuinely different catalogs needs a staging account whose two teams have distinct featureModelChoice admin settings — that's account provisioning, not something a capture pass in this environment can produce. Flagging for a human decision on whether it's needed before merge.

Testing

Since this branch is stacked four deep, it gets no real CI matrix; all checks ran locally on this exact tip:

  • cargo check --workspace --all-targets — clean.
  • cargo clippy -p warp --lib --tests -- -D warnings — clean.
  • ./script/format — clean.
  • ./script/bundle --channel oss --nouniversal --check-only — clean.
  • cargo clippy --locked --target wasm32-unknown-unknown --profile release-wasm-debug_assertions -- -D warnings — clean.
  • Targeted cargo nextest run -p warp ... — 1099 passed.
  • No UI surface changed; no screenshots.

Linked Issue

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

Adds Team.feature_model_choice and Workspace.feature_model_choice,
sourced from the server's per-team/workspace Team.featureModelChoice
(new teams[].featureModelChoice query field, schema mirror line) and
the existing Workspace.featureModelChoice, via gql_convert's new
feature_model_choice_from_gql. Persisted through new
feature_model_choice_json sqlite columns (migration included) so the
warm-start cache round-trips it too.

Nothing reads this yet -- LLMPreferences keeps master's flat
models_by_feature and behaves exactly as today. This PR is inert by
design: dead data plus a conversion test
(team_feature_model_choices_conversion_keeps_each_teams_choice_distinct)
proving each team keeps its own payload through the fold.
@warp-agent-staging
warp-agent-staging Bot force-pushed the factory/multi-team-pr3a-catalog-team-scope branch from 1d8e82a to fc3d9d1 Compare August 26, 2026 06:02
Seeds Team/Workspace.feature_model_choice from the legacy,
pre-team-keyed MODELS_BY_FEATURE_CACHE_KEY cache as a one-release
migration in UserWorkspaces::new: a Workspace restored from the
SQLite cache predates the feature_model_choice column (or the app
hasn't fetched since upgrading), so its catalog is still the bare
default, and this seeds it from the last-known model list instead so
an offline launch right after upgrading isn't stuck on just the auto
default for the rest of the offline session.

migrate_legacy_feature_model_choices_cache understands both real
shapes an older client could have written: the (more recent) single
ModelsByFeature, and (older still) a bare AvailableLLMs, which
becomes the agent_mode field.

Still nothing reads Team/Workspace.feature_model_choice for model
selection -- LLMPreferences keeps master's flat models_by_feature.
legacy_cache_migration_yields_a_usable_teamless_catalog proves the
older bare-AvailableLLMs shape migrates to a catalog whose agent_mode
bucket resolves the cached model.
Deletes LLMPreferences's flat, non-team-aware models_by_feature field
and its MODELS_BY_FEATURE_CACHE_KEY read/write, and the old
WorkspacesMetadataResponse.feature_model_choices field PR 1 left
alone. Every accessor now reads through UserWorkspaces instead,
resolved against a caller-supplied TeamScope (or a raw team uid):

- UserWorkspaces gains feature_model_choice_for_scope/_for_team_uid
  (reads, via the same scoped_or_workspace_setting pattern every
  other team-scoped setting uses; the absent/no-workspace-at-all case
  falls back to the resolved-teamless `pre_login_models_by_feature`
  seeded by PR 1/PR 2's migration or a pre-login fetch) and
  set_feature_model_choice_for_team_uid (the narrow one-off
  authed-refresh write path).
- get_llm_info/_for_team_uid/_for_scope, get_active_base_model, and
  the various choice/default getters gained an app/ctx parameter and
  a TeamScope parameter accordingly, with the ~85 call sites across
  the app updated: terminal/, settings_view/, ai/blocklist/,
  ai/agent_sdk/, pane_group/, search/, workspace/, and
  crates/warp_tui/.
- Preserves the earlier review-round behavioral fixes carried since
  PR 1's first draft: teamless eager fetch for agent_mode_evals
  builds, refresh-through-workspace-metadata (not the unscoped
  get_feature_model_choices) for blocklist triggers, cross-team
  get_llm_info display lookups, and stale-bucket eviction (implied by
  the wholesale Team/Workspace replacement on every response).

This is the bulk of the three-PR stack, but every design decision
here -- the storage shape, the legacy-cache conversion -- was already
reviewed in PR 1 (#15463) and PR 2 (#15565); this should read as fan-out
from those two, not new design.
Adds LLMPreferences::get_llm_info_for_scope/_for_team_uid for callers
that already have a team scope and need policy-affecting metadata
(disable_reason, host_configs), and migrates the Bedrock login
banner (terminal/view.rs), Grok/GEAP routing checks
(response_stream.rs), and the model picker (profile_model_selector.rs)
to it.

Threads the caller's live TeamScope through
AIExecutionProfileAppExt's context-window helpers
(configurable_context_window, context_window_display_value,
context_window_limit_for_request, should_show_long_context_pricing_warning)
and their execution_profiles/settings_view call sites, and adds
RequestParams::team_uid so ai/agent/api.rs's request-limit clamp reads
the same team's context-window range the request was built against.

Builds on PR1's Team/Workspace-based catalog placement (rather than the
earlier team-keyed LLMPreferences cache); the scoped lookups added here
read through the same UserWorkspaces-backed accessors.

Comes with its own tests: the two-team context-window clamp
regression, and the scoped-lookup assertions in the catalog
distinctness/pruning test.
@warp-agent-staging
warp-agent-staging Bot force-pushed the factory/multi-team-pr3b-scoped-llm-lookups branch from 68bf503 to a2e36c1 Compare August 26, 2026 07:07
@warp-agent-staging
warp-agent-staging Bot changed the base branch from factory/multi-team-pr3a-catalog-team-scope to factory/multi-team-pr3c-move-llm-preferences August 26, 2026 07:07
@warp-agent-staging warp-agent-staging Bot changed the title [multi-team P3b] Scope LLM lookups and context-window limits to the window's team [multi-team P3d] Scope LLM lookups and context-window limits to the window's team Aug 26, 2026
oz-agent and others added 7 commits August 26, 2026 11:51
… home (#15565)

## Description
Stacked on #15463 (PR 1 of the three-PR stack — "Parse and store the
team catalog into UserWorkspaces"). PR 1 added
`Team`/`Workspace.feature_model_choice` but nothing populates it for a
user restored from a pre-upgrade SQLite cache. PR 3 (to follow, stacked
on this branch) moves `LLMPreferences` off its flat catalog onto these
fields; this PR makes sure a warm-started, pre-upgrade cache has
something real to read once that happens.

Seeds `Team`/`Workspace.feature_model_choice` from the legacy,
pre-team-keyed `MODELS_BY_FEATURE_CACHE_KEY` cache as a one-release
migration in `UserWorkspaces::new`: a `Workspace` restored from the
SQLite cache predates the `feature_model_choice` column (or the app
hasn't fetched since upgrading), so its catalog is still the bare
default. This seeds it from the last-known model list instead, so an
offline launch right after upgrading isn't stuck on just the `auto`
default for the rest of the offline session.

`migrate_legacy_feature_model_choices_cache` understands both real
shapes an older client could have written: the (more recent) single
`ModelsByFeature`, and (older still) a bare `AvailableLLMs`, which
becomes the `agent_mode` field.

Still nothing reads `Team`/`Workspace.feature_model_choice` for model
selection — `LLMPreferences` keeps master's flat `models_by_feature`.
`legacy_cache_migration_yields_a_usable_teamless_catalog` proves the
older bare-`AvailableLLMs` shape migrates to a catalog whose
`agent_mode` bucket resolves the cached model.

## Testing
- `cargo check --workspace --all-targets` — clean.
- `cargo clippy -p warp --lib --tests -- -D warnings` — clean.
- `./script/format` — clean.
- `./script/bundle --channel oss --nouniversal --check-only` — clean.
- `cargo clippy --locked --target wasm32-unknown-unknown --profile
release-wasm-debug_assertions -- -D warnings` — clean.
- `cargo nextest run -p warp -E 'test(user_workspaces) or
test(legacy_cache) or test(gql_convert)'` — 107 passed.
- No UI surface changed; this is a data-layer change, so no screenshots.
- This branch is stacked on a non-default base, so it does not get a
real CI matrix in this repo; the above was run locally on this exact
branch tip.

## Linked Issue
- [x] N/A — stacked continuation of #15463.

## Agent Mode
- [x] Warp Agent Mode - This PR was created via Warp's AI Agent Mode

---------

Co-authored-by: warp-agent-staging[bot] <240773466+warp-agent-staging[bot]@users.noreply.github.com>
Co-authored-by: Oz <oz-agent@warp.dev>
…team-scope' into pr3-work

# Conflicts:
#	app/src/workspaces/gql_convert.rs
#	app/src/workspaces/user_workspaces/mod.rs
# Conflicts:
#	app/src/workspaces/user_workspaces/mod.rs
#	app/src/workspaces/user_workspaces/user_workspaces_tests.rs
Base automatically changed from factory/multi-team-pr3c-move-llm-preferences to master August 27, 2026 03:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant