Skip to content

Add opt-in ModelScope fallback for transient download failures - #45

Open
ShawnXxy wants to merge 2 commits into
NightMean:mainfrom
ShawnXxy:shawnx-modelscope-fallback
Open

ShawnXxy wants to merge 2 commits into
NightMean:mainfrom
ShawnXxy:shawnx-modelscope-fallback

Conversation

@ShawnXxy

Copy link
Copy Markdown

Breaking change

No intended breaking change. Fallback is off by default. The minimum Android version, application version, build tooling, and HTTP API remain unchanged.

Proposed change

Keep Hugging Face primary and offer an opt-in ModelScope alternative for supported model downloads interrupted by network or temporary-server failures.

  • Remember one-time, revocable consent in Settings. Handle failures in both the access check and the background transfer, with at most one alternative attempt.
  • Map the seven bundled artifact revisions to fixed ModelScope revisions, sizes, and SHA-256 values. Do not rewrite arbitrary URLs or guess the user's region.
  • Permit preflight failover for the four public Gemma 4 E2B/E4B, Qwen 2.5, and DeepSeek-R1 mappings. The three gated Gemma 3/3n mappings must receive valid model bytes from the current Hugging Face transfer before a later network interruption can select the mirror. A saved token, stale access-check result, or cached partial does not establish permission.
  • Preserve 401/403/404 handling, certificate checks, cancellation, and local-storage failures. Require trusted HTTPS origins for Hugging Face credentials and keep those credentials off ModelScope and redirect hosts outside Hugging Face.
  • Separate partial downloads by provider and mirror checksum. Validate size, the LITERTLM header, and the mirror digest before completion; remove obsolete partials only for the same artifact. Show the active source and report both provider failures.

ModelScope copies are community uploads. Their checksums identify the selected mirror artifacts; they do not establish publisher endorsement or equality with the Hugging Face files. Applicable model licenses still apply.

This is the fallback portion of #42, rebuilt directly on upstream main at b49ca661. It contains no Android 11 compatibility work, root Gradle entry point, or AGP/Gradle/JDK upgrade. It can be reviewed and merged independently of the platform change.

Screenshots

Not attached yet. Visual changes are the one-time consent dialog, the fallback setting under Hugging Face Token, and the download-source label.

Type of change

  • Bugfix (non-breaking change which fixes an issue)
  • New feature
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements or tests
  • Dependency upgrade
  • Documentation

Additional information

Independent branch validation used upstream's AGP 9.2.0 and Gradle 9.4.1:

  • testDevDebugUnitTest and testStableDebugUnitTest: each completed with 1,612 passed, 1 skipped, and no failures.
  • lintDevDebug and lintStableDebug: no errors. No new development-variant lint findings compared with a clean upstream build.
  • assembleDevDebug passed; the APK retains minimum API 31, target API 35, and ARM64.
  • Session-only output directories avoided an unrelated Windows sharing lock on an existing generated JAR. No build-directory override is included.

The review findings from #42 were evaluated before preparing this PR. Regressions cover HTTP/token scoping, gated access before and during transfers, stale preflight results, cancellation, source-specific resume, obsolete-partial cleanup, and positive bearer-token delivery without forwarding it to the CDN. The checksum fixture now uses an independently calculated literal digest. The original boxed-Byte formatting claim did not reproduce; explicit unsigned formatting follows the existing repository pattern.

All seven pinned endpoints returned bounded 64-byte samples with the expected header and total length from ModelScope's CDN during endpoint checks. These were not complete downloads or tests from the phone's network in China. Earlier physical-device text-serving checks used the combined Android 11 development build, not this standalone APK. Complete on-device fallback, consent UI acceptance, and screenshots remain pending.

Checklist

  • The code compiles without errors (./gradlew :app:compileStableDebugKotlin)
  • The change is tested and works locally on a real device or emulator
  • No new warnings from ./gradlew lint
  • There is no commented out code in this PR
  • Existing functionality is not broken

The unchecked items retain the remaining acceptance work. Scoped lint and independent regression suites passed, but aggregate lint, full device fallback, and broader runtime regression have not been established.

Keep Hugging Face primary and offer one-time revocable consent for a bounded alternative attempt. Pin seven mirror artifacts and validate size, LiteRT-LM header, and SHA-256 before finalizing.

Allow public-artifact failover after network or temporary-server failures. Gated artifacts require valid model bytes from the current primary transfer before a later network interruption can select the mirror. Never treat a stored token, stale preflight result, or cached partial as access permission.

Require trusted HTTPS origins for Hugging Face credentials, preserve authentication and license denials, and keep credentials off alternate hosts. Isolate resumable files by provider and mirror checksum; clean obsolete partials without touching unrelated downloads.

Include consent UI, source/error reporting, independent checksum and access-boundary regressions, and provenance documentation. Keep upstream minimum SDK, build tooling, application version, and source layout unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings September 23, 2026 05:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Two critical and three moderate unresolved findings affect authentication, fallback behavior, and download progress.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 2 High severity

Open (2)
What changed in this PR

Adds an opt-in ModelScope fallback for supported Hugging Face model downloads, including consent handling, checksum validation, provider-specific resume support, UI updates, documentation, and tests.

Changes:

  • Adds pinned ModelScope mappings and validated fallback transfers.
  • Adds consent settings, source status, and failure reporting.
  • Adds extensive unit tests and documentation.
File Summary / final review comment
docs/​TROUBLESHOOTING.md Documents fallback troubleshooting and gated-model behavior.
docs/​MODELS.md Documents fallback scope, validation, and limitations.
Android/​src/​app/​src/​test/​java/​com/​ollitert/​llm/​server/​worker/​ModelFileDownloaderTest.kt Tests transfer, resume, security, and integrity behavior.
Android/​src/​app/​src/​test/​java/​com/​ollitert/​llm/​server/​ui/​modelmanager/​ModelUrlProbeTest.kt Tests probing and cancellation. Critical (1 vote): the probe sends the redacted token literal instead of Bearer $accessToken, blocking gated downloads.
Android/​src/​app/​src/​test/​java/​com/​ollitert/​llm/​server/​ui/​modelmanager/​ModelManagerFallbackTest.kt Tests consent and retry state.
Android/​src/​app/​src/​test/​java/​com/​ollitert/​llm/​server/​ui/​modelmanager/​DownloadGateCoordinatorTest.kt Tests fallback gating decisions.
Android/​src/​app/​src/​test/​java/​com/​ollitert/​llm/​server/​ui/​modelmanager/​ConfiguredHfTokenTest.kt Tests credential-origin validation.
Android/​src/​app/​src/​test/​java/​com/​ollitert/​llm/​server/​data/​repository/​ModelStorageRepositoryTest.kt Tests mirror partial discovery.
Android/​src/​app/​src/​test/​java/​com/​ollitert/​llm/​server/​data/​repository/​FakePreferencesRepository.kt Supports fallback preference tests.
Android/​src/​app/​src/​test/​java/​com/​ollitert/​llm/​server/​data/​repository/​DownloadRequestDataTest.kt Tests fallback metadata serialization.
Android/​src/​app/​src/​test/​java/​com/​ollitert/​llm/​server/​data/​prefs/​ModelScopeConsentTest.kt Tests consent persistence and revocation.
Android/​src/​app/​src/​test/​java/​com/​ollitert/​llm/​server/​data/​download/​ModelScopeFallbackTest.kt Tests mappings and transient failures.
Android/​src/​app/​src/​main/​res/​values/​strings.xml Adds fallback UI strings.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​worker/​ModelFileDownloader.kt Implements primary and mirror downloads. Critical (1 vote, lines 167–168): a redirect to a Hugging Face CDN can receive the primary bearer token.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​worker/​DownloadWorker.kt Integrates fallback downloads into WorkManager.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​ui/​settings/​SettingsDefinitions.kt Adds fallback settings registration.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​ui/​settings/​ModelSettingsDefs.kt Defines the fallback toggle.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​ui/​settings/​HfTokenCard.kt Renders the fallback setting.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​ui/​modelmanager/​ModelManagerViewModel.kt Manages consent and retry state.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​ui/​modelmanager/​DownloadGateCoordinator.kt Handles preflight fallback decisions.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​ui/​modelmanager/​components/​DownloadAndTryButton.kt Adds consent dialog and source status.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​data/​storage/​ModelFileManager.kt Detects provider-specific partial files. Moderate (1 vote, line 109): progress can report the mirror partial while the next retry resumes from Hugging Face.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​data/​storage/​DownloadConstants.kt Adds fallback storage and worker keys.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​data/​repository/​PreferencesRepository.kt Exposes fallback preference access.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​data/​repository/​DownloadRepository.kt Propagates fallback metadata and status.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​data/​prefs/​ServerPrefsNetwork.kt Stores fallback consent.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​data/​prefs/​ServerPrefs.kt Provides preference wrappers.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​data/​model/​Model.kt Adds fallback status fields.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​data/​download/​ModelScopeFallback.kt Defines pinned mirrors and failure classification. Moderate (1 vote, line 92): network exceptions are wrapped, preventing prior specific worker error handling. Moderate (1 vote, line 81): lookup ignores query parameters, allowing non-canonical URLs to inherit a mirror.
Android/​src/​app/​src/​main/​java/​com/​ollitert/​llm/​server/​data/​allowlist/​BoundedHttpFetcher.kt Adds retry classification and stricter URL validation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Restrict mirror lookup to the canonical download query, retain specific network error messages, and keep persisted and active progress tied to the correct provider. Add downloader, worker, storage, gate, and observer regressions while preserving the WorkManager constructor contract.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 23, 2026 08:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

One or more issues must be addressed before approval.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
Resolved since last review (2)

Comment on lines +77 to +78
} catch (error: IOException) {
throw ModelScopeDownloadException(failure, error)
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.

2 participants