Skip to content

feat(wizard): validate the LLM credential during onboarding, with failure recovery#4055

Open
Harshvardhan86 wants to merge 13 commits into
Tracer-Cloud:mainfrom
Harshvardhan86:issue/3591-wizard-llm-key-validation
Open

feat(wizard): validate the LLM credential during onboarding, with failure recovery#4055
Harshvardhan86 wants to merge 13 commits into
Tracer-Cloud:mainfrom
Harshvardhan86:issue/3591-wizard-llm-key-validation

Conversation

@Harshvardhan86

Copy link
Copy Markdown
Contributor

Fixes #3591

Describe the changes you have made in this PR -

The onboarding wizard was the one credential-entry surface in the product that saved its credential without validating it. Every other credential in the same wizard (Dagster, Slack, GitLab, OpenSearch, Telegram) already prompts → validates → retries on failure — the LLM key skipped that convention, so a typo'd key sailed through onboarding and only surfaced later, deep inside an investigation. If the keyring write failed, the wizard hard-exited with no way to recover.

This PR brings the LLM credential up to the same standard:

  • Validate against the model you actually picked. Model selection is now hoisted above the key prompt in both the change-provider and saved-provider branches, so the live probe exercises the model that will be persisted — not the provider default. (Validating the default would, for example, tell every Ollama user to pull a model they never selected.)
  • A shared recovery menu. On a validation failure the user gets one menu — Re-enter the key (default) / Save anyway without validating / Pick a different provider — and the same menu shape now handles a keyring-persist failure (Retry saving / Continue for this session only / Pick a different provider). One helper, both failure sites; the three previous bare return 1 exits are gone.
  • No lockout. Validation collapses a bad key, a dead network, a corporate proxy, a rate limit, and a capped account into the same failure, so a hard gate would lock out a user with a valid key. "Save anyway" is always available (with an explicit unverified warning), and "Continue for this session only" exports the key to the environment for the current run without ever writing it to .env.
  • An honest summary. The final screen reports where the credential actually went: system keychain, system keychain (unverified), project .env, or not saved — re-enter next run.

Two latent bugs that gating-on-validation would have weaponized are fixed in the same change: a valid MiniMax key was being sent to api.openai.com (its base URL was never wired in), and pressing Enter at the Azure key prompt silently persisted the placeholder endpoint URL as the API key. A typo'd Azure endpoint is now re-promptable on retry/repick instead of being an unrecoverable dead-end.

Tests: a new autouse socket guard makes any real network call in a wizard test a hard failure (the validator caches SDK classes in module globals, so a mis-patched test could otherwise hit api.anthropic.com with a live key and pass for the wrong reason). Every new behaviour ships with a test written to fail first. make lint && make format-check && make typecheck && make test-scope all green (857 passed, 3 skipped); the wizard suite is 224 passing.

Known follow-up (not in this PR): after 10 consecutive failed retries the wizard returns exit 1 instead of dropping back to the provider menu — bounded, since the escape hatches are offered every round. Out of scope: the two onboarding GIFs in docs/quickstart.mdx record the old key→model order and are now slightly stale (the prose is order-agnostic); validating an already-stored key that is never re-prompted; and a pre-existing double-Ctrl-C-exits-0 behaviour.

Demo/Screenshot for feature changes and bug fixes -

▶️ A ~40s terminal recording of the wizard is attached below, plus a screenshot of the recovery menu. It shows a fresh onboarding: the model is chosen first, a bad key is rejected live (✗ Anthropic · claude-opus-4-7 · Failed), the recovery menu appears, "Save anyway" completes onboarding, and the summary honestly reports unverified.


Code Understanding and AI Usage

Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?

  • No, I wrote all the code myself
  • Yes, I used AI assistance (continue below)

If you used AI assistance:

  • I have reviewed every single line of the AI-generated code
  • I can explain the purpose and logic of each function/component I added
  • I have tested edge cases and understand how the code handles them
  • I have modified the AI output to follow this project's coding standards and conventions

Explain your implementation approach:

The problem is that the wizard persisted the LLM API key straight to the keyring with no live check, while every other integration in the same wizard already validated-then-retried — so a bad key only failed much later. My approach was to reuse that existing pattern (validate_provider_credentials already existed and is used by opensre auth) rather than invent anything new.

The one non-obvious decision was ordering: the key was prompted before the model was chosen, so a naive "validate before save" would have probed the provider's default model. That breaks Ollama (fails on a model the user never pulled), so I hoisted model selection above the credential block in both branches. I considered hard-gating onboarding on a passing validation, but rejected it because the validator can't distinguish a bad key from a dead network / proxy / rate-limit / capped account — a hard gate would lock out real users, so "Save anyway" is mandatory and "Re-enter" is the default.

Key pieces: _prompt_validated_llm_credential (the validate-then-retry loop), _recovery_action (one shared 3-verb menu used by both the validation-failure and keyring-persist-failure sites), and _persist_llm_credential_with_recovery (replaces the three bare hard-exits). The continue_unsaved path exports to os.environ only and is re-applied after sync_provider_env so the in-process shell handoff can read it, but is never written to .env.


Checklist before requesting a review

  • I have added proper PR title and linked to the issue
  • I have performed a self-review of my code
  • I can explain the purpose of every function, class, and logic block I added
  • I understand why my changes work and have tested them thoroughly
  • I have considered potential edge cases and how my code handles them
  • If it is a core feature, I have added thorough tests
  • My code follows the project's style guidelines and conventions

Note: Please check Allow edits from maintainers if you would like us to assist in the PR.

Wire validate_provider_credentials into run_wizard's two credential
collection sites via _collect_validated_llm_credential, with a bounded
failure menu (re-enter / save anyway / repick) and a keyring-persist
recovery menu replacing the hard exit. Fixes Tracer-Cloud#3591.
…d tests

The working tree was reverted by a concurrent session between the
original restore and commit; this reinstates the verified state
(flow.py wiring, 67-test contract, amended onboarding smoke tests)
from the tagged recovery point wave-3591-true-state.
…L gap

Socket-level autouse guard for the wizard package: no wizard test may open a
real outbound connection. This is load-bearing for Tracer-Cloud#3591 — validation.py caches
the provider SDK classes in module globals, tests/conftest.py loads .env with
override=True, and dev machines routinely export ANTHROPIC_API_KEY, so a test
that patches the wrong name would quietly call api.anthropic.com with a live key
and still go green. The guard derives from BaseException so the SDKs' own
`except Exception` (which re-raises transport faults as APIConnectionError)
cannot swallow it.

It immediately caught a pre-existing impure test: run_wizard's model-change test
reaches _configure_grafana_local -> seed_logs -> wait_for_loki -> requests.get
against a local Loki. The connection error is swallowed today, so it passes —
but on a machine with Loki running, that unit test seeds a live Loki. Fixed in
the follow-up commit.

Also adds the RED tests for the MiniMax gap: `minimax` is a live api_key
provider that reaches the wizard's key prompt, but _get_provider_base_url has no
branch for it, so a valid key is sent to api.openai.com and rejected.
MINIMAX_BASE_URL has been in config all along, never wired in. Latent today;
a hard block once onboarding gates on validation.

Refs Tracer-Cloud#3591.
Deltas 3-6 + X2. Every new test fails on missing behaviour (AssertionError)
or on a helper the approved design contract obliges GREEN to create.
@github-actions

Copy link
Copy Markdown
Contributor

Greptile code review

This repo uses Greptile for automated review. Before merge, aim for Confidence Score: 5/5 with zero unresolved review threads — see CONTRIBUTING.md.

Run a review — add a PR comment with:

@greptile review

Give it ~5-10 minutes (sometimes longer) for results, then fix feedback and re-trigger until you reach Confidence Score: 5/5.

Optional: automate with the greploop skill.

@Harshvardhan86

Copy link
Copy Markdown
Contributor Author
opensre-3591-recovery-menu
opensre-3591-demo.mp4

Demo — a ~40s terminal recording of the wizard on this branch. Fresh onboarding: the model is chosen first, a typo'd key is rejected live (✗ Anthropic · claude-opus-4-7 · Failed — the failure names the model we validated against), the recovery menu appears, "Save anyway" completes onboarding, and the summary honestly reports unverified. Recorded against the real Anthropic API (a bad key returns a genuine 401).

@Harshvardhan86

Copy link
Copy Markdown
Contributor Author

@greptile review

@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR brings LLM credential validation in the onboarding wizard up to the same standard as every other integration: model selection is hoisted before the credential prompt so the live probe runs against the model that will be persisted, and a shared three-verb recovery menu handles both validation failures and keyring-persist failures. Two latent bugs are fixed in the same change: MiniMax was being validated against api.openai.com (its base URL was never wired in), and pressing Enter at the Azure key prompt silently persisted the placeholder endpoint URL as the API key.

  • New llm_credential.py module extracts credential prompting, validation, and persistence logic from flow.py, providing _prompt_validated_llm_credential (validate-then-retry loop) and _persist_llm_credential_with_recovery (shared recovery menu for both failure sites).
  • validation.py now includes MINIMAX_BASE_URL and a parameterized coverage gate (test_every_openai_compatible_provider_has_a_base_url) to catch future providers added without a base URL.
  • tests/cli/wizard/conftest.py introduces an autouse BaseException-derived socket guard that makes any real outbound connection from a wizard test a hard test failure, preventing mis-patched tests from silently hitting live endpoints.

Confidence Score: 5/5

Safe to merge — the new validate-then-retry loop is well-guarded (escape hatches at every failure site, no hard gate), the MiniMax and Azure placeholder fixes are correct, and the test coverage is thorough.

The new validate-then-retry loop is well-guarded with escape hatches at every failure site, the MiniMax and Azure placeholder fixes are correct, and the test coverage is thorough with the socket guard ensuring mis-patched tests fail loudly. All findings are non-blocking: an inaccurate module name in the conftest error message and a misleading recovery-menu label for the uncommon Ollama .env write-failure path.

tests/cli/wizard/conftest.py — the remediation text in the socket-guard error message references the wrong module. surfaces/cli/wizard/llm_credential.py — the "Retry saving to the system keychain" label should be conditioned on credential_kind for Ollama.

Important Files Changed

Filename Overview
surfaces/cli/wizard/llm_credential.py New module encapsulating LLM credential prompt/validate/persist logic; one minor UX issue: the "Retry saving to the system keychain" recovery-menu label is inaccurate when the failing credential is a host-kind (Ollama .env write).
surfaces/cli/wizard/flow.py Wizard control flow updated to hoist model selection before credential block in both the change-provider and saved-provider branches; Azure double-call is intentional and commented; session_env_sink re-application after sync_provider_env is correct.
surfaces/cli/wizard/validation.py MiniMax base URL wired in via MINIMAX_BASE_URL; comment on line 181-182 accurately documents the silent-fallback risk for providers missing from _get_provider_base_url.
tests/cli/wizard/conftest.py New autouse BaseException-derived socket guard correctly blocks all real outbound connections; error message contains wrong module reference for patching guidance (says flow instead of llm_credential).
tests/cli/wizard/test_flow.py Comprehensive new tests covering validation failures, save-anyway, continue-unsaved, Azure endpoint re-prompt, keyring failure recovery, and credential-state summary lines; autouse stub correctly patches llm_credential.validate_provider_credentials.
tests/cli/test_smoke.py Smoke tests updated to reflect new model-before-key order and correct "could not be verified. What next?" recovery prompt text; CLI provider repick test updated with correct step ordering and prompt text.
tests/cli/wizard/test_validation.py New validation unit tests including the MiniMax base-URL regression test and a parameterized coverage gate for all OpenAI-compatible providers.
docs/quickstart.mdx Added two new troubleshooting bullets covering the "key could not be verified" and "could not save key" recovery flows; prose is accurate and matches actual wizard behavior.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[run_wizard: LLM Provider step] --> B{change_provider?}
    B -- Yes --> C[Choose provider + auth method]
    C --> D{is Azure?}
    D -- No --> E[choose_provider_model]
    D -- Yes --> F[defer model pick into credential block]
    E --> G[_prompt_validated_llm_credential]
    F --> G
    B -- No --> H[use saved provider/model]
    H --> I{has_api_key?}
    I -- No --> G
    I -- Yes --> K

    G --> G1[prompt credential]
    G1 --> G2[ensure_azure_endpoint_settings]
    G2 -- None / back --> REPICK
    G2 -- ok --> G3{is Azure?}
    G3 -- Yes --> G4[export key to env, choose_azure_deployment]
    G3 -- No --> G5[validate_provider_credentials]
    G4 --> G5
    G5 -- ok --> PERSIST
    G5 -- fail --> G6{recovery menu}
    G6 -- Re-enter --> G7[clear Azure endpoint env, retry loop]
    G7 --> G1
    G6 -- Save anyway --> PERSIST
    G6 -- Repick --> REPICK
    G6 -- Escape --> CANCEL

    PERSIST[_persist_llm_credential_with_recovery] --> P1{write ok?}
    P1 -- Yes + validated --> RET_OK[return OK]
    P1 -- Yes + unvalidated --> RET_UNVERIFIED[return UNVERIFIED]
    P1 -- No --> P2{persist recovery menu}
    P2 -- Retry --> PERSIST
    P2 -- Continue unsaved --> P3[export to os.environ only, return UNSAVED]
    P2 -- Repick --> REPICK
    P2 -- Escape --> CANCEL

    RET_OK --> K[sync_provider_env]
    RET_UNVERIFIED --> K
    P3 --> K
    K --> L[re-apply session_env_sink if UNSAVED]
    L --> M[Integrations step]
    M --> N[Summary: system keychain / unverified / .env / not saved]

    REPICK --> A
    CANCEL --> EXIT1[return 1]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[run_wizard: LLM Provider step] --> B{change_provider?}
    B -- Yes --> C[Choose provider + auth method]
    C --> D{is Azure?}
    D -- No --> E[choose_provider_model]
    D -- Yes --> F[defer model pick into credential block]
    E --> G[_prompt_validated_llm_credential]
    F --> G
    B -- No --> H[use saved provider/model]
    H --> I{has_api_key?}
    I -- No --> G
    I -- Yes --> K

    G --> G1[prompt credential]
    G1 --> G2[ensure_azure_endpoint_settings]
    G2 -- None / back --> REPICK
    G2 -- ok --> G3{is Azure?}
    G3 -- Yes --> G4[export key to env, choose_azure_deployment]
    G3 -- No --> G5[validate_provider_credentials]
    G4 --> G5
    G5 -- ok --> PERSIST
    G5 -- fail --> G6{recovery menu}
    G6 -- Re-enter --> G7[clear Azure endpoint env, retry loop]
    G7 --> G1
    G6 -- Save anyway --> PERSIST
    G6 -- Repick --> REPICK
    G6 -- Escape --> CANCEL

    PERSIST[_persist_llm_credential_with_recovery] --> P1{write ok?}
    P1 -- Yes + validated --> RET_OK[return OK]
    P1 -- Yes + unvalidated --> RET_UNVERIFIED[return UNVERIFIED]
    P1 -- No --> P2{persist recovery menu}
    P2 -- Retry --> PERSIST
    P2 -- Continue unsaved --> P3[export to os.environ only, return UNSAVED]
    P2 -- Repick --> REPICK
    P2 -- Escape --> CANCEL

    RET_OK --> K[sync_provider_env]
    RET_UNVERIFIED --> K
    P3 --> K
    K --> L[re-apply session_env_sink if UNSAVED]
    L --> M[Integrations step]
    M --> N[Summary: system keychain / unverified / .env / not saved]

    REPICK --> A
    CANCEL --> EXIT1[return 1]
Loading

Reviews (6): Last reviewed commit: "merge: reconcile #4055 extraction with u..." | Re-trigger Greptile

Comment thread tests/cli/test_smoke.py Outdated
Tracer-Cloud#3591)

Greptile P1: the parametrized codex/opencode repick smoke test was missed when
the smoke tests were reordered for Tracer-Cloud#3591. It still expected the pre-hoist order
(key prompt, then model) and the stale string 'failed validation. What next?'.
Post-hoist the real flow is model -> key -> 'could not be verified. What next?',
matching test_onboard_interactive_smoke. The test is skipped without codex or
opencode on PATH, so the staleness never failed locally; it would hang until
timeout once either CLI is installed.
Comment thread surfaces/cli/wizard/flow.py Outdated
@Harshvardhan86

Copy link
Copy Markdown
Contributor Author

@greptile review

Fixed the P1 in 61b8c86d: the CLI-repick smoke test now uses the model-before-key order and the correct could not be verified. What next? prompt, matching test_onboard_interactive_smoke.

@Devesh36

Copy link
Copy Markdown
Collaborator

@greptile review again

@muddlebee

Copy link
Copy Markdown
Collaborator

@Harshvardhan86 nice work, will take a deeper look later 👍

@Tracer-Cloud Tracer-Cloud deleted a comment from greptile-apps Bot Jul 15, 2026
Comment thread surfaces/cli/wizard/flow.py Outdated
)


def _recovery_action(*, prompt: str, retry_label: str, retry_hint: str, escape: Choice) -> str:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

all such logic should be extracted to a different file? could you check where it fits appropriately, create new file if necessary

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good call — moved the whole LLM-credential cluster out of flow.py into a new surfaces/cli/wizard/llm_credential.py in 8671b4df: the validate-then-retry prompt, the recovery menu, the render/persist/summary helpers, and the Azure endpoint helpers. run_wizard just imports and calls them now, so flow.py drops ~460 lines and the credential logic lives in one place.

Comment thread surfaces/cli/wizard/flow.py Outdated


def _render_credential_validation(label: str, model: str, result: ValidationResult) -> None:
"""Render the live-probe outcome, naming the provider *and* the probed model.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

same for all such functions in this class, cehck once

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same — this one moved too; it's all in llm_credential.py now (8671b4df).

Comment thread surfaces/cli/wizard/flow.py Outdated
credential_outcome = _prompt_validated_llm_credential(
provider, model=model, session_env_sink=session_env_sink
)
if credential_outcome == "cancel":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

use constants pls for such keyworks like "cancel", "repick" and re-use acorss this PR

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 8671b4df — the outcome keywords are now named constants in llm_credential.py (RETRY / REPICK / CANCEL / SAVE_ANYWAY / CONTINUE_UNSAVED, plus the OK / UNSAVED / UNVERIFIED states) and reused across the PR instead of the string literals. The values are unchanged so the menu default (retry) and the outcome checks still line up.

Comment thread surfaces/cli/wizard/flow.py Outdated
migration_outcome = _persist_llm_credential_with_recovery(
provider, legacy_api_key, session_env_sink=session_env_sink
)
if migration_outcome == "cancel":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

same

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same — covered by the constants in 8671b4df.

@cerencamkiran

Copy link
Copy Markdown
Collaborator

The recovery flow handles keyring failures, but I think there is still a gap for host-based credentials. _persist_llm_credential() calls sync_env_values() for values like OLLAMA_HOST without handling write errors. If writing to .env fails, the user may get an exception instead of the retry menu. Could this use the same recovery flow? It would also be good to add a test for a .env write failure.

Harshvardhan86 and others added 2 commits July 15, 2026 16:04
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ts; host .env recovery (Tracer-Cloud#4055 review)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Harshvardhan86

Copy link
Copy Markdown
Contributor Author

@cerencamkiran nice catch — you're right, the host path (OLLAMA_HOST.env) could raise instead of showing the menu if the .env write failed. Fixed in 8671b4df: the host branch now catches the write error, prints a .env-path warning, and returns into the same recovery menu (Retry saving / Continue for this session only / Pick a different provider) rather than letting the exception escape. Added a test that forces a .env write failure (test_run_wizard_ollama_host_env_write_failure_offers_recovery_menu). Thanks for spotting it!

@Harshvardhan86

Copy link
Copy Markdown
Contributor Author

@greptile review

Addressed the review in 8671b4df: extracted the LLM-credential helpers into surfaces/cli/wizard/llm_credential.py, replaced the outcome-string literals with named constants, and fixed the host .env-write path to fall into the recovery menu (with a test). Details in the inline replies above.

@cerencamkiran

Copy link
Copy Markdown
Collaborator

Could you please resolve the conflicts? Thanks!

…lm-key-validation

# Conflicts:
#	tests/cli/wizard/test_flow.py
@Harshvardhan86

Copy link
Copy Markdown
Contributor Author

@cerencamkiran done — merged latest main in and resolved the conflict (it was a single import line in test_flow.py after your #4069 configurators split; flow.py auto-merged). Wizard suite is green locally. Thanks for the nudge!

@Harshvardhan86

Copy link
Copy Markdown
Contributor Author

@muddlebee — addressed your review in 8671b4df (pulled the credential helpers into llm_credential.py, and the outcome keywords are constants now), and just merged latest main to clear the conflict. Greptile's back to 5/5. Ready for another look whenever you have time — no rush 🙏

@muddlebee

Copy link
Copy Markdown
Collaborator

@Harshvardhan86 thank you I will test this locally :)

@Tracer-Cloud Tracer-Cloud deleted a comment from Davidson3556 Jul 17, 2026
…nai module (Tracer-Cloud#4117)

Delegate llm_credential.py's Azure helpers to upstream's new azure_openai
module: drop the duplicated endpoint helpers, import ensure_endpoint_settings
and choose_provider_model, and route the Azure deployment pick through
choose_provider_model so Tracer-Cloud#4117's live deployment discovery is preserved.

The deployment pick moves inside _prompt_validated_llm_credential (after
endpoint+key collection, before validation) so Tracer-Cloud#3591's "validate the model the
user picked" invariant holds while discovery has its credentials. All Tracer-Cloud#3591
fixes survive: model-before-validate, shared recovery menu, continue_unsaved,
honest summary, Azure empty-key default, Azure endpoint re-prompt on
retry/repick, and host .env-write recovery.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Harshvardhan86

Copy link
Copy Markdown
Contributor Author

Merged latest main and reconciled with #4117llm_credential.py now delegates Azure endpoint + model selection to the new azure_openai.py module (so deployment discovery is preserved), and I dropped my duplicated Azure helpers. For Azure the deployment pick happens after the endpoint + key are entered (discovery needs them) but still before validation, so the "validate the model you picked" behaviour holds. Wizard suite green locally; conflicts resolved.

@Harshvardhan86

Copy link
Copy Markdown
Contributor Author

@greptile review

Re-running on the reconcile merge (d1b27b57) — merged latest main and integrated #4117's new azure_openai.py (llm_credential.py now delegates Azure endpoint + deployment discovery to it).

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.

Better LLM key failure UX during onboarding

4 participants