Skip to content

copilot: check the VS Code extension's requests against a real gateway - #1362

Draft
xi-db wants to merge 6 commits into
mainfrom
xi-db/stack/vscode-live-wire
Draft

xi-db wants to merge 6 commits into
mainfrom
xi-db/stack/vscode-live-wire

Conversation

@xi-db

@xi-db xi-db commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

🥞 Stacked PR

Use this link to review incremental changes.


Summary

npm run live sends the requests this extension builds to a real AI Gateway and checks the replies. Everything the extension does today is tested against hand-written bodies and a stubbed vscode module, so nothing checks that the gateway actually accepts what we send — which is where every bug this extension has had so far came from: a thinking model's content blocks, images inside tool results, and an output limit the gateway refuses.

What it covers

  • A streamed answer on each route the extension uses, asserted on a marker the model is asked to echo, so stray text can't pass.
  • A forced tool call per route, with its arguments parsed into an object.
  • An image inside a tool_result, where the reply has to name the image's colour. This is the part each route builds differently.
  • A prompt-cache read: an identical second Anthropic turn must report cache_read_input_tokens > 0.
  • The refused-output-limit retry: a deliberately impossible max_tokens must still produce an answer.
  • A Model Provider Service's Claude models on Anthropic Messages and its OpenAI models on the gateway's OpenAI route, plus two negative controls — the same request without the routing header must fail, and must also fail on the mlflow/v1 route. Without those a 200 would prove nothing, since the gateway could have answered from system.ai while the routing was dead.

How it is wired

  • A new live npm script bundles test/live/requests.live.ts with the same esbuild flags as test, so the live cases run the same sources that ship. The offline suite globs test/*.test.ts and never collects it.
  • The workspace, models and services come from UG_LIVE_* variables; a case whose variables are unset isn't registered, so the CI job decides what runs.
  • A new vscode-extension-live job runs it with the workspace secrets, using the same services and targets as the integration CUJs. It doesn't run for pull requests from forks, which don't get secrets, or in the merge queue.
  • The token comes from UG_LIVE_AUTH_COMMAND or UG_LIVE_TOKEN, so the extension's own auth.ts is exercised rather than bypassed.

Each run costs a few cents and takes about 20 seconds.

Testing

  • npm run live against a staging workspace: 5 cases, all passing in 18.5 s — the Claude turn plus its cache read, the Claude tool call and the image in its tool result, the output-limit retry, the GPT turn and tool call on Responses, and the tool-result image on Responses.
  • The Model Provider Service cases and the negative controls have not run locally: no service I can reach from a developer workspace answers, which is what motivated this PR. They first execute in CI, so expect this job to need a fix or two before it settles.
  • No live chat_completions case is registered: every OpenAI-type model on the CI workspace is GPT-5 or newer, which ug routes to Responses. Covering that wire needs a GPT-4-class target added to a CI service.
  • Offline suite unchanged: 38 tests pass, tsc clean.

Stacked on #1358, which adds the Model Provider Service support and the Anthropic Messages route this exercises.

This pull request and its description were written by Isaac.

xi-db and others added 2 commits October 29, 2026 18:33
… and Claude on Anthropic Messages

ug configure --agents vscode takes --provider <catalog.schema.name> or
--model-location <catalog.schema>, picking the models the way ug copilot does.
The extension sends a provider's header, uses the gateway's OpenAI route for
its OpenAI models, and calls every Claude model through Anthropic Messages with
prompt-cache points.

Co-authored-by: Isaac <no-reply@databricks.com>
…s, retry a refused output limit

A model's real output limit can be below the one ug configures, which Anthropic
Messages rejects; the gateway names the limit it allows, so retry once with it.
--provider and --model-location now refuse any other model agent in the same
run, and the per-model api picks its route through a lookup.

Co-authored-by: Isaac <no-reply@databricks.com>
@xi-db xi-db added the ug-review Run the automated UG review label Oct 29, 2026
@xi-db
xi-db changed the base branch from xi-db/stack/vscode-mps to main October 29, 2026 21:29
Comment thread src/ucode/cli.py
if agent is not None and agents is not None:
raise RuntimeError("Use either --agent or --agents, not both.")
if provider is not None or model_location is not None:
requested_agents = [a.strip().lower() for a in (agents or "").split(",") if a.strip()]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maintainability · low
can we parse agents into one list up front and reuse it here and in the requested split below? right now the same split runs twice, and the two copies could drift apart.

Comment on lines +300 to +301
} else if (last?.role === role) {
last.content.push(...content);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · low
what happens if a user text message is followed by a user message with tool results, e.g. after an assistant turn was skipped as empty? the merged message puts tool_result after the text, and anthropic rejects that with a 400. can we hoist tool_result blocks to the front when merging?

if (!allowed) {
throw new Error(`AI Gateway returned ${response.status}: ${detail}`);
}
response = await post(token, { ...body, max_tokens: allowed });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

performance · medium
can we remember allowed per model? anthropic models with no known limit get the 32K default, so every turn hits a 400 and then retries. that adds a round-trip to each request.

@github-actions

Copy link
Copy Markdown
Contributor

Polly AI Review

Blocking issues

None confirmed.

Security vulnerabilities

No confirmed vulnerability found. The live job excludes fork PRs and merge-queue events and disables checkout credential persistence. Secret scoping can still be tightened as noted below.

Non-blocking notes

  • vscode-extension/test/live/requests.live.ts:180 — can we exclude 429/5xx from the negative controls? They currently pass on unrelated gateway failures, so they don't establish routing rejection.
  • tests/test_agent_vscode.py:500 — can we retain a successful no---provider input in test_configure_agents_vscode_sets_up_the_workspace_then_vscode? Both variants now bypass that CLI path.
  • .github/workflows/ci.yml:135 — can we scope UG_LIVE_* secrets to the live-test step? Job-level variables unnecessarily expose the gateway token to npm ci lifecycle scripts.

Approach

The approach is sound: reuse production authentication, request builders and stream parsers, test format details offline, and reserve live tests for actual gateway acceptance, routing and image interpretation. No materially simpler replacement is evident.

Summary

Diff size (computed): 16 files; +990 / -123 lines (1113 changed text lines); 0 binary files.

Category Files Added Deleted Share of changed text lines
Tests and test support 4 595 34 56.5%
Documentation 1 27 5 2.9%
Dependencies and lockfiles 1 2 1 0.3%
Implementation / other 10 366 83 40.3%

Shares use additions + deletions, including generated text and lockfiles. Binary files have no line count. Tests include fixtures/helpers under test paths and colocated test/spec files; categories are path-based, not coverage measurements.

Changes

The intended outcome is to verify that requests built by the VS Code extension work against a real gateway, including Model Provider Service routing.

The full snapshot includes the inherited provider/location configuration and Anthropic Messages implementation, request retry handling, shared catalog helpers, live harness, CI job, documentation, package metadata and generated extension bundle.

The breadth is justified by exercising those production paths. No concrete implementation removal was identified that preserves the stated outcome. Keeping the existing MPS foundation separate in the stack remains useful for review and reversion.

Tests

Test-by-test assessment

An isolated, in-memory runner executed the frozen-head vscode-extension/test/formats.test.ts: 20 cases passed. Separate isolated probes exercised output-limit recovery and same-role message merging. These checks do not establish a successful standard build or live run.

Python tests, extension component tests, typecheck and live gateway tests were not executed during this review. The PR reports local live results but explicitly leaves MPS cases and negative controls unverified locally.

Test / case Behavior protected Layer Needed? Action / rationale
tests/test_agent_vscode.py — code fixture and added catalog imports Shared catalog lookup without real authentication Unit setup keep Extends existing setup with one token-lookup patch; no separate fixture framework.
tests/test_agent_vscode.py — test_installs_the_extension_with_the_workspace_and_its_claude_and_gpt_models Default-source configuration, model families, Anthropic/GPT API selection and exclusions Component keep Extends existing installation assertions for the changed catalog contract.
tests/test_agent_vscode.py — test_models_with_the_same_label_show_their_ids Distinguishable labels after catalog sorting Unit keep Preserves duplicate-label coverage while adapting to the shared catalog.
tests/test_agent_vscode.py — test_a_provider_services_claude_targets_with_a_200k_cap_and_its_name Claude MPS filtering, limits, provider configuration and service attribution Component keep Covers the provider-backed configuration boundary without live setup.
tests/test_agent_vscode.py — test_a_openai_provider_services_models_on_responses_and_chat_completions OpenAI MPS discovery and API selection Component keep Protects the Responses versus Chat Completions split.
tests/test_agent_vscode.py — test_keeps_the_installed_setup_for_a_provider_service_it_cant_use Unsupported provider, relayed-auth and listing failures preserve installed setup Component keep Parameterized failures share the preservation contract and retain distinct inputs.
tests/test_agent_vscode.py — test_a_model_locations_services_with_a_200k_cap Schema-scoped discovery, fallback API, limits and absence of a provider header Component keep Location-backed configuration differs materially from MPS routing.
tests/test_agent_vscode.py — test_configure_agents_vscode_refuses_a_workspace_the_machine_isnt_managed_for Managed-workspace boundary CLI keep Signature-only adaptation preserves the existing guard.
tests/test_agent_vscode.py — test_configure_agents_vscode_sets_up_the_workspace_then_vscode Provider forwarding and dry-run behavior CLI keep Restore a successful default-source input alongside the provider variants; direct configure coverage does not replace CLI coverage.
tests/test_agent_vscode.py — test_configure_refuses_a_model_source_vscode_cant_take Invalid agent/source combinations, conflicting source flags and malformed schema CLI keep The parameterized inputs protect distinct public-input boundaries at the appropriate layer.
vscode-extension/test/extension.test.ts — stubFetch Observation of provider routing headers Component setup keep Adds the minimal capture needed for routing assertions.
vscode-extension/test/extension.test.ts — yields each event's JSON until [DONE] SSE event decoding and termination Unit keep Path-argument adaptation preserves existing stream coverage.
vscode-extension/test/extension.test.ts — keeps a last event that has no trailing newline Final SSE event retention Unit keep Preserves an important stream-boundary assertion.
vscode-extension/test/extension.test.ts — reports other gateway errors with their body Gateway failure details reach the caller Component keep Preserves the error-reporting contract after URL construction changes.
vscode-extension/test/extension.test.ts — retries once with a new token after a 401, then asks the user to sign in Authentication renewal and bounded failure Component keep Existing coverage remains applicable to the changed request path.
vscode-extension/test/extension.test.ts — retries once with the output limit the gateway allows, and reports any other error Output-limit rewriting, recovery and bounded retry Component keep Smallest reliable layer for inspecting retry payloads and counts.
vscode-extension/test/extension.test.ts — keeps well-formed models, names, families and token limits, the provider, and trims the workspace URL Parsing new configuration fields and API values Unit keep Extends the existing configuration-validation scenario.
vscode-extension/test/extension.test.ts — gives no models for a missing or malformed file Safe configuration defaults, including an empty provider Unit keep Preserves malformed/missing-file behavior.
vscode-extension/test/extension.test.ts — settings, answers, askEach helpers Shared configuration and request execution for routing checks Component setup keep Consolidates repeated setup while leaving routing assertions in the tests.
vscode-extension/test/extension.test.ts — lists the configured models by name, or by id without one, or none with a hint when settings are missing Model listing, family overrides and missing-settings guidance Component keep Updates existing expectations for the new API selection.
vscode-extension/test/extension.test.ts — sends each model's turns to its API with the User-Agent and its output limit Default-source routing, attribution and output limits Component keep Covers all three request routes without requiring gateway access.
vscode-extension/test/extension.test.ts — routes a Model Provider Service's models with its header, and OpenAI ones on the OpenAI route MPS header ownership and OpenAI endpoint selection Component keep Protects a distinct routing contract.
vscode-extension/test/formats.test.ts — cached constant and Anthropic suite setup Shared cache-control expectations Unit setup keep Small shared expectation; no excessive setup introduced.
vscode-extension/test/formats.test.ts — sends the system prompt, conversation, images and tools, with cache points after the system prompt and the last message Anthropic request encoding, cache points and tool choice Unit keep Pure serialization is best checked here rather than through live responses.
vscode-extension/test/formats.test.ts — puts a tool's images in its result, results before text, and joins messages of the same role Tool-result image encoding, ordering and same-role merging Unit keep Protects distinct conversion behavior with explicit request assertions.
vscode-extension/test/formats.test.ts — leaves out empty messages, the system prompt and tools when there are none Empty-input and omitted-field boundaries Unit keep Covers request-shape boundaries independently of gateway availability.
vscode-extension/test/formats.test.ts — streams text, drops thinking, and assembles tool calls from their fragments once the message stops Anthropic event conversion and completed tool-call assembly Unit keep Fragment handling needs deterministic stream inputs.
vscode-extension/test/formats.test.ts — fails on a token-limit stop, a refusal, any other stop reason, an error event, and a stream without message_stop Failure mapping and rejection of incomplete tool calls Unit keep Consolidates related parser failures while preserving distinct triggers.
vscode-extension/test/live/requests.live.ts — env, configuration guard, send, turnOf, textOf, callsOf, image fixtures, viewImageTool, marker, echoTurn, imageTurn, route wrappers Environment-driven live execution using production code Live setup keep Shared transport and input construction are justified; assertions remain in individual cases.
vscode-extension/test/live/requests.live.ts — streams a Claude answer on Anthropic Messages, and reads the prompt cache on a repeat Real Anthropic acceptance, streamed content and cache usage Live integration keep Offline tests cannot establish actual cache reads.
vscode-extension/test/live/requests.live.ts — gets a Claude tool call, and an image inside its result reaches the model Real forced tool call and interpretation of a tool-result image Live integration keep Verifies a gateway/model boundary that serialized-shape assertions cannot establish.
vscode-extension/test/live/requests.live.ts — retries with the output limit the gateway allows instead of failing the turn Compatibility with the gateway's actual output-limit rejection Live integration keep Complements the component test's deterministic retry assertions.
vscode-extension/test/live/requests.live.ts — streams a GPT answer and a tool call on Responses Real Responses acceptance, streaming and tool parsing Live integration keep Checks production request/parser compatibility with the actual route.
vscode-extension/test/live/requests.live.ts — sends a tool result's image in the request Responses accepts Responses-specific tool-result image encoding Live integration keep Distinct wire representation warrants its own live assertion.
vscode-extension/test/live/requests.live.ts — streams an answer on Chat Completions Real Chat Completions route acceptance Live integration keep Useful optional coverage; the checked-in CI configuration does not enable it.
vscode-extension/test/live/requests.live.ts — serves its Claude models on Anthropic Messages, with tool calls Real Claude MPS routing and tool use Live integration keep Mocked headers cannot prove that the service actually receives the request.
vscode-extension/test/live/requests.live.ts — needs the routing header, and isn't served on the mlflow route Missing-header and wrong-route rejection for Claude MPS Live integration keep Tighten error assertions: current substring matching also accepts 429/5xx.
vscode-extension/test/live/requests.live.ts — serves its OpenAI models on the gateway's OpenAI route Real OpenAI MPS endpoint and routing acceptance Live integration keep Exercises the separate OpenAI service path.
vscode-extension/test/live/requests.live.ts — isn't served on the mlflow route Wrong-route rejection for OpenAI MPS Live integration keep Tighten error assertions so transient/server failures cannot satisfy the control.

Existing authentication tests cover token caching, concurrent acquisition, forced renewal and command failure. Existing Chat Completions format coverage remains; the new live Chat Completions case is not enabled in CI. No separate browser or native VS Code E2E setup is needed for these HTTP-format contracts.

To verify in an authorized environment, run uv run pytest tests/test_agent_vscode.py, then run npm run typecheck and npm test from vscode-extension. With the documented UG_LIVE_* settings, run npm run live and confirm that MPS positives and routing-specific negatives actually execute.

Scope

All changes plausibly support the stated outcome, including the inherited configuration/routing foundation and generated bundle. No clearly unrelated changes were found. The absence of a linked issue does not create a scope concern because the description provides a concrete requested outcome.


Automated review by Polly · workflow run

Co-authored-by: Isaac <no-reply@databricks.com>
@xi-db
xi-db force-pushed the xi-db/stack/vscode-live-wire branch from e678d82 to b67321a Compare October 30, 2026 09:14
xi-db and others added 3 commits October 30, 2026 09:17
…ond set of names

Co-authored-by: Isaac <no-reply@databricks.com>
npm run live sends the requests the extension builds to a real AI Gateway: a
streamed turn and a tool call on each route, an image inside a tool result,
a prompt-cache read, the refused-output-limit retry, and that a Model Provider
Service's models are served only with its routing header and not on the mlflow
route. A new CI job runs it with the workspace secrets, and skips where those
aren't available.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
@xi-db
xi-db force-pushed the xi-db/stack/vscode-live-wire branch from b67321a to 7c189d1 Compare October 30, 2026 09:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ug-review Run the automated UG review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant