feat: add hailuo-web (MiniMax web) chat provider (#6673)#7734
Open
diegosouzapw wants to merge 1 commit into
Open
feat: add hailuo-web (MiniMax web) chat provider (#6673)#7734diegosouzapw wants to merge 1 commit into
diegosouzapw wants to merge 1 commit into
Conversation
Adds hailuo-web as a new free web-cookie chat provider targeting the
MiniMax consumer chat product at hailuo.ai (chat.minimax.io), distinct
from the existing paid API-key minimax/minimax-cn providers.
Ported from the g4f reference implementation
(g4f/Provider/needs_auth/mini_max/{HailuoAI,crypt}.py):
- MD5-chain request signing (generate_yy_header/get_body_to_yy)
- Custom event:/data: SSE parsing (send_result/message_result/close_chunk),
where message_result.content is a cumulative snapshot diffed into deltas
- Device-fingerprint query params, derived deterministically per-connection
from the token when the user hasn't captured the real browser values
New catalog entry, executor, registry entry, dispatch wiring, tests
(17 cases covering signing test vectors independently verified via
Python hashlib.md5, SSE parsing, streaming/non-streaming dispatch, and
401-terminal vs 429-transient error mapping), and a regenerated
provider-translate-path golden snapshot (purely additive diff).
Contributor
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
| } | ||
|
|
||
| function md5(input: string): string { | ||
| return createHash("md5").update(input, "utf8").digest("hex"); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
hailuo-webas a new free,_token-based web-cookie chat provider targeting the MiniMax consumer chat product at hailuo.ai (chat.minimax.io) — distinct from the existing paid API-keyminimax/minimax-cnproviders. Follows the establishedkimi-web/doubao-web/qwen-webweb-cookie pattern perCLAUDE.md→ "Adding a New Provider".Refs #6673
Protocol source
Ported directly from the g4f reference implementation (
g4f/Provider/needs_auth/mini_max/{HailuoAI,crypt}.py), re-fetched and re-verified byte-for-byte during this implementation (not just the earlier triage summary):POST https://www.hailuo.ai/v4/api/chat/msg?<fingerprint query>tokenheader — the_tokenvalue from the site'slocalStorage, plus a per-requestyysignature header.yy = MD5(urlencode(path_and_query) + "_" + bodyToYy + MD5(timestamp) + "ooui"), wherebodyToYy = MD5(characterID) + MD5(msgContent) + MD5(chatID) + MD5(""). Ported with Node's built-incrypto.createHash("md5")— no new dependency, noeval.characterID,msgContent,chatID,searchMode).event:/data:SSE lines —send_result(chat title + chatID, once) andmessage_result(a cumulativecontentsnapshot per event, diffed into deltas here) until aclose_chunkevent ends the stream.This resolves 3 of the 4 "unresolved unknowns" the triage plan flagged (exact API host, auth header shape, doc/API path) with concrete evidence from the g4f source rather than guesses. The 4th (exact model id list) turned out to be moot: the g4f provider exposes a single
default_modelwith no model-selection query param in the request at all — so this ships one catalog model (hailuo) rather than a guessed MiniMax-M-series list.Device-fingerprint fields (
device_id,uuid, os/browser name, screen dims) are normally generated by the browser and stored inlocalStorage. When the user hasn't captured the real values, the executor derives stable per-connection ones from the token via MD5 (pure function — same token always yields the same fingerprint, no server-side state needed).What was deferred / remains unverified
The signing scheme and SSE parsing are pure functions, fully unit-tested. What is not verified is the live upstream handshake:
www.hailuo.aiis still the current live host and/v4/api/chat/msgthe current live path (both taken from the actively-maintained g4f source, not from a fresh live capture).device_id/uuidis accepted by the upstream, or whether it strictly requires the browser-generated values fromlocalStorage(theauthHinttells users to capture_token; device fingerprint is best-effort/derived).aiohttp-based client sends.Live check needed before this is fully verified: deploy to the VPS (
root@192.168.0.15), configure ahailuo-webconnection with a real_tokencaptured from a logged-in hailuo.ai session (DevTools → Application → Local Storage), and send a real chat request end-to-end, e.g.:If the live capture shows a different signing order, an additional required header, or a different device-fingerprint requirement,
open-sse/executors/hailuo-web.tsneeds a follow-up fix before this is treated as production-verified.How validated (Hard Rule #18 — TDD)
New test file
tests/unit/executor-hailuo-web.test.ts(17 cases), including:getBodyToYy/generateYyHeadersignature test vectors independently re-derived via Python'shashlib.md5+urllib.parse.quote(not asserted against a guessed literal) — the exact commands are documented as comments in the test file.pyQuotepercent-encoding matches Python'squote(s, safe="")exactly (notencodeURIComponent, which leaves!*'()unescaped).message_result.content→ delta diffing.data:line that must not throw.RESILIENCE_GUIDE.md).fetch.Red → green proof:
open-sse/executors/hailuo-web.tsdoes not exist onorigin/release/v3.8.49(git show origin/release/v3.8.49:open-sse/executors/hailuo-web.ts→ "exists on disk, but not in ...") — the new test file'simport(...)of that module would fail before this change, and all 17 cases pass after it:Also extended
tests/unit/provider-alias-uniqueness.test.ts(appended, not editing existing lines) to pin thathailuo-webresolves to its own id/alias and does not collide withminimax/minimax-cn.Gates run (all green)
node scripts/check/check-test-discovery.mjsnpm run typecheck:core/npm run typecheck:noimplicit:core— clean on all touched files (pre-existing unrelatednoimpliciterrors incombo.ts/usageTracking.ts/cliRuntime.tsconfirmed pre-existing on base viagit show origin/release/v3.8.49:<path>)npx eslint --suppressions-location config/quality/eslint-suppressions.json <changed files>— 0 errorsnpm run check:complexity-ratchets— green (had to extractexecute()intoprepareMsgContent/buildSignedRequest/dispatch/buildStreamingResponse/buildNonStreamingResponsehelpers to get under the 80-line/15-complexity/15-cognitive caps)node scripts/check/check-file-size.mjs— OK, no frozen-file growthnpm run check:cycles— no new cyclesnpm run check:provider-consistency— OKtests/snapshots/provider/translate-path.json) regenerated viaUPDATE_GOLDEN=1 node --import tsx/esm --test tests/unit/provider-translate-path-golden.test.ts— diff confirmed purely additive (only the newhailuo-webentry), then re-ran withoutUPDATE_GOLDENto confirm greendocs/reference/PROVIDER_REFERENCE.mdregenerated vianpm run gen:provider-reference(auto-generated — not hand-edited); unrelated entries in the diff (kimi-coding rename, agnes/dahl/xai-oauth additions) are pre-existing catalog state the doc was simply one day stale on, not introduced by this PRnode scripts/check/check-fabricated-docs.mjs --strict— the only 3 failures are the pre-existing base-red claims called out in the task brief (being fixed separately in docs: fix three stale references failing the fabricated-docs gate #7728), unrelated to this changeNot run:
npm run test:coverage(heaviest gate, shared box per project convention — CI runs it authoritatively).i18n
Checked
src/i18n/messages/en.jsonfor a provider-label pattern (kimiWebLabel,doubaoWebLabel, etc.) — confirmed these keys are unused dead/legacy entries (no source references them anywhere), and most recent web-cookie providers (grok-web, deepseek-web, adapta-web, yuanbao-web, gemini-business, etc.) don't have entries either. No new i18n keys needed; the dashboard renders provider names straight from the catalognamefield.Not in scope for this PR
npm run check:docs-countsreports pre-existing drift (README.md/AGENTS.md/CLAUDE.md hardcode "265 providers", real count is now 269) — this drift predates this PR (base was already at 268 vs. the claimed 265) and updating those 3 files' prose isn't part of the "Adding a New Provider" recipe; left untouched as out-of-scope pre-existing debt.