Skip to content

feat: add MiniMax M3 as a cloud AI provider#324

Open
M3NT1 wants to merge 3 commits into
JerryZLiu:mainfrom
M3NT1:feat/minimax-m3-provider-v2
Open

feat: add MiniMax M3 as a cloud AI provider#324
M3NT1 wants to merge 3 commits into
JerryZLiu:mainfrom
M3NT1:feat/minimax-m3-provider-v2

Conversation

@M3NT1

@M3NT1 M3NT1 commented Jul 19, 2026

Copy link
Copy Markdown

What

This PR is a re-port of the MiniMax M3 provider integration onto the current main (v2.0.3) base, which refactored the provider system into LLMProviderID with separate cases for chatGPT, claude, openAICompatible, and local. It also brings over the model-management + usage-analytics layer that was in the original PR #320.

The user's request:

"Sync the upstream changes into the repo while keeping our MiniMax changes intact. And while you're at it, add a model picker that fetches live, a dashboard for provider/model usage, and a badge on each card."

Why a new branch?

PR #320 was opened against the v2.0.0 base. Since then upstream has landed 163 commits — including a major provider refactor that split chatGPTClaude into separate chatGPT / claude cases, introduced openAICompatible and local cases, added LLMProviderSetupPreferences, and rebuilt onboarding/settings around a new ProviderSetupState machine.

A straight rebase produced 21 conflicting files; rather than fight through them, this branch is a clean re-application onto the v2.0.3 base so both upstream's evolution and our MiniMax additions land together. #320 should be closed in favour of this PR.

Changes

Core/AI/LLMTypes.swift — added .minimax case to LLMProviderID with analytics + display label.

Core/AI/MiniMaxProvider*.swift (8 new files) — the provider itself, modelled on OllamaProvider but with Bearer auth against https://api.minimax.io/v1 and a 1M-token context window. Five files: main + Networking, Reasoning (strips <think>…</think> defensively), Summaries, Transcription. Plus MiniMaxModelPreference, MiniMaxPromptPreferences, and Utilities/MiniMaxAPIHelper.

Core/AI/ModelCatalog.swift (new) — actor that fetches the current model list from each provider's API on the fly:

  • GET /v1/models for MiniMax (OpenAI-compatible)
  • GET /v1beta/models?key=… for Gemini (filtered to vision-capable)
  • GET {baseURL}/api/tags for Ollama, GET /v1/models for LM Studio
  • Curated lists for ChatGPT/Claude CLIs (no public list endpoint)
  • 24h cache in UserDefaults. On failure, falls back to a curated list with a banner in the UI.

Core/AI/LLMService.swift — new makeMiniMaxProvider() wired into both makeBatchProvider and makeTextProvider switch statements. A new providerModelId(for:) helper centralises the per-provider model lookup. A new attachMeta helper stamps every card before persistence.

Core/Analytics/ProviderStatsCalculator.swift (new) — pure data layer that turns the local TimelineCard list into a ProviderStatsSummary:

  • byProvider: [String: Int] (always includes all known providers, even at zero)
  • byModel: [(providerID, modelID, count)]
  • daily: [DailyStat] (last 365 days)
  • weekly: [WeeklyStat] (last 26 weeks)
  • monthly: [MonthlyStat] (last 12 months)
  • heatmapDays: [HeatmapDay] (last 365 days, intensity 0…1)

Views/UI/Dashboard/ProviderStatsView.swift (new) — self-contained dashboard surface with:

  • 4 stat tiles (total cards, active providers, peak day, tracking since)
  • Provider bar chart + percentages
  • Per-(provider, model) breakdown
  • GitHub-style 365-day heatmap (5 intensity buckets, 7×N grid)
  • Daily / weekly / monthly bar chart with Week / Month / Quarter / Year range picker
  • Manual refresh button

Storage layerprovider_id + model_id columns on timeline_cards with ALTER TABLE migration + composite index. TimelineCard and TimelineCardShell gained matching fields. New StorageManager.fetchAllTimelineCards() powers the dashboard.

Onboarding — 6th provider card on the comparison screen (sparkles icon, "MiniMax M3", positioned between OpenAI-compatible and Local). New 4-step setup flow: get-key → enter-key → test-connection → complete. Reuses the existing apiKey state field that the input view binds to.

Settings → Providers tab — MiniMax section with model picker (live or curated), keychain API-key field, test-connection button that re-opens the provider-setup modal. Prompt-override plumbing added for symmetry with the other providers.

Per-card badgeCanvasActivityCard (and equivalent list/grid renderers) now show a small text pill below the time label, e.g. MiniMax · MiniMax-M3, Claude · Sonnet 4.5, Local · qwen3-vl:4b. Falls back to "Unknown" when the card predates the schema migration.

ChatCLIProvider — gained a defaultModel: String? parameter that overrides the previously hard-coded "sonnet" / "haiku" / "gpt-5.4" strings. LLMService.makeChatCLIProvider reads llmCodexModel / llmClaudeModel from UserDefaults and passes them through.

Privacy

  • The MiniMax API key is stored in the macOS Keychain, not UserDefaults
  • Screenshots are sent to https://api.minimax.io/v1/chat/completions and nowhere else
  • The provider-stats dashboard reads only the local SQLite DB — no network, no screenshot content, no PII
  • Failure paths log HTTP status + body up to 64 KB via LLMLogger, but no PII

Build verification

xcodebuild -project Dayflow.xcodeproj -scheme Dayflow \
  -configuration Release -destination 'platform=macOS' \
  CODE_SIGN_IDENTITY="-" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO \
  -quiet build
# ** BUILD SUCCEEDED **

The pre-existing Swift 6 / Sendable warnings in ChatCLIProcessRunner.swift, MiniMaxProvider+Transcription.swift, etc. are unchanged from upstream and not introduced by this PR.

M3NT1 added 3 commits July 19, 2026 20:42
Ports the MiniMax integration from JerryZLiu#320 onto the v2.0.3 base, which
refactored the provider system into LLMProviderID with separate cases for
chatGPT, claude, openAICompatible, and local.

What's included

- New LLMProviderID.minimax case with analytics + display label
- MiniMaxProvider + 4 extension files (Networking, Reasoning, Summaries,
  Transcription) following the OllamaProvider pattern but using Bearer
  auth against https://api.minimax.io/v1 with M3's 1M-token context
- MiniMaxModelPreference, MiniMaxPromptPreferences, MiniMaxAPIHelper
- LLMService.makeMiniMaxProvider() wired into makeBatchProvider and
  makeTextProvider; providerModelId(for:) helper stamps cards with the
  active model
- Storage layer: provider_id + model_id columns on timeline_cards with
  ALTER TABLE migration, TimelineCard/TimelineCardShell fields, and
  fetchAllTimelineCards() for the dashboard
- Onboarding: 6th provider card (sparkles icon) + dedicated 4-step setup
  flow (get-key, enter-key, test, complete)
- Settings: MiniMax section in providers tab with model picker, keychain
  field, test-connection button, and prompt-override plumbing
- ModelCatalog + ProviderStatsCalculator + ProviderStatsView (dashboard
  data + GitHub-style heatmap + daily/weekly/monthly breakdowns)

Build verified: xcodebuild Release succeeds, no new errors vs v2.0.3 base
The toast that warns the user about missing screen-recording access was
gated on 'getSavedPreference() == true || appState.isRecording', but the
recorder forces isRecording off the moment it detects the permission is
missing, and getSavedPreference() is nil for users who have never
explicitly toggled it. Net effect: the notice never surfaced for the
exact case it was meant to alert on, and the user had no UI signal that
recording was broken (the Resume button appeared to do nothing).

Switch the guard to the 'didOnboard' UserDefaults flag so the notice
appears whenever the user has completed onboarding and the permission
is missing. Also re-evaluate the notice on every tab change to .timeline
so a user who lands on the timeline after dismissing elsewhere still
sees it (the session-dismiss flag prevents spam).
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.

1 participant