Skip to content

refactor: modularize providers constants (non-stacked) - #3994

Closed
oyi77 wants to merge 5 commits into
diegosouzapw:release/v3.8.28from
oyi77:pr/providers
Closed

oyi77 wants to merge 5 commits into
diegosouzapw:release/v3.8.28from
oyi77:pr/providers

Conversation

@oyi77

@oyi77 oyi77 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Replaces #3794 as an independent, non-stacked PR branched from release/v3.8.27.

Extracts 3.1K-line providers.ts constants into modular structure.

Supersedes: #3794

@oyi77
oyi77 requested a review from diegosouzapw as a code owner June 16, 2026 12:14

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request refactors and modularizes the AI provider definitions by splitting them into dedicated files under src/shared/constants/providers/ (e.g., apiKey.ts, audio.ts, local.ts, oauth.ts, etc.) and exposing them via a central index. The review feedback highlights critical inconsistencies in duplicate key and alias resolution strategies across the combined provider sections, suggesting a consistent first-one-wins approach. Additionally, the reviewer recommends renaming the duplicate huggingchat and phind keys in WEB_COOKIE_PROVIDERS to avoid collisions, updating the optional API key utility accordingly, and adding corresponding unit tests to comply with the repository style guide when modifying production code.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +30 to +38
function getOrCreateAiProviders(): Record<string, any> {
if (!_aiProviders) {
_aiProviders = {};
for (const section of _PROVIDER_SECTIONS) {
Object.assign(_aiProviders, section);
}
}
return _aiProviders;
}

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.

high

There is a critical inconsistency in how duplicate keys across sections are resolved. getProviderById uses nullish coalescing (??) in forward order, resulting in a first-one-wins strategy (returning the APIKEY_PROVIDERS version of huggingchat). However, getOrCreateAiProviders loops forward and uses Object.assign, resulting in a last-one-wins strategy (overwriting earlier definitions with the WEB_COOKIE_PROVIDERS version). This causes getProviderById("huggingchat") and AI_PROVIDERS["huggingchat"] to return different objects. To align them, we should loop backwards when building the combined maps so that earlier sections consistently take priority.

Suggested change
function getOrCreateAiProviders(): Record<string, any> {
if (!_aiProviders) {
_aiProviders = {};
for (const section of _PROVIDER_SECTIONS) {
Object.assign(_aiProviders, section);
}
}
return _aiProviders;
}
function getOrCreateAiProviders(): Record<string, any> {
if (!_aiProviders) {
_aiProviders = {};
for (let i = _PROVIDER_SECTIONS.length - 1; i >= 0; i--) {
Object.assign(_aiProviders, _PROVIDER_SECTIONS[i]);
}
}
return _aiProviders;
}

Comment on lines +42 to +52
function getOrCreateAliasToId(): Record<string, string> {
if (!_ALIAS_TO_ID) {
_ALIAS_TO_ID = {};
for (const section of _PROVIDER_SECTIONS) {
for (const p of Object.values(section)) {
if ((p as any).alias) _ALIAS_TO_ID[(p as any).alias] = (p as any).id;
}
}
}
return _ALIAS_TO_ID;
}

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.

high

To align with the first-one-wins resolution strategy of getProviderByAlias, getOrCreateAliasToId should also prioritize earlier sections in _PROVIDER_SECTIONS when resolving duplicate aliases (like poe or v0). Looping backwards ensures that the first definition's alias mapping is preserved.

Suggested change
function getOrCreateAliasToId(): Record<string, string> {
if (!_ALIAS_TO_ID) {
_ALIAS_TO_ID = {};
for (const section of _PROVIDER_SECTIONS) {
for (const p of Object.values(section)) {
if ((p as any).alias) _ALIAS_TO_ID[(p as any).alias] = (p as any).id;
}
}
}
return _ALIAS_TO_ID;
}
function getOrCreateAliasToId(): Record<string, string> {
if (!_ALIAS_TO_ID) {
_ALIAS_TO_ID = {};
for (let i = _PROVIDER_SECTIONS.length - 1; i >= 0; i--) {
for (const p of Object.values(_PROVIDER_SECTIONS[i])) {
if ((p as any).alias) _ALIAS_TO_ID[(p as any).alias] = (p as any).id;
}
}
}
return _ALIAS_TO_ID;
}

Comment on lines +56 to +66
function getOrCreateIdToAlias(): Record<string, string> {
if (!_ID_TO_ALIAS) {
_ID_TO_ALIAS = {};
for (const section of _PROVIDER_SECTIONS) {
for (const p of Object.values(section)) {
_ID_TO_ALIAS[(p as any).id] = (p as any).alias || (p as any).id;
}
}
}
return _ID_TO_ALIAS;
}

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.

high

Similarly, getOrCreateIdToAlias should be updated to loop backwards through _PROVIDER_SECTIONS to maintain consistency with the first-one-wins strategy used by getProviderById and getProviderAlias.

Suggested change
function getOrCreateIdToAlias(): Record<string, string> {
if (!_ID_TO_ALIAS) {
_ID_TO_ALIAS = {};
for (const section of _PROVIDER_SECTIONS) {
for (const p of Object.values(section)) {
_ID_TO_ALIAS[(p as any).id] = (p as any).alias || (p as any).id;
}
}
}
return _ID_TO_ALIAS;
}
function getOrCreateIdToAlias(): Record<string, string> {
if (!_ID_TO_ALIAS) {
_ID_TO_ALIAS = {};
for (let i = _PROVIDER_SECTIONS.length - 1; i >= 0; i--) {
for (const p of Object.values(_PROVIDER_SECTIONS[i])) {
_ID_TO_ALIAS[(p as any).id] = (p as any).alias || (p as any).id;
}
}
}
return _ID_TO_ALIAS;
}

Comment on lines +172 to +186
huggingchat: {
id: "huggingchat",
// "hc" belongs to the hackclub provider; huggingchat uses its own id as alias.
alias: "huggingchat",
name: "HuggingChat (Free)",
icon: "auto_awesome",
color: "#FFD21E",
textIcon: "HC",
website: "https://huggingface.co/chat",
hasFree: true,
freeNote: "Free LLM chat — no subscription required. Rate limits apply.",
authHint:
"Paste your hf-chat cookie value from huggingface.co/chat (DevTools → Application → Cookies → hf-chat). Optional — works without auth for basic use.",
riskNoticeVariant: "webCookie",
},

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.

medium

The keys huggingchat and phind are duplicated between APIKEY_PROVIDERS and WEB_COOKIE_PROVIDERS. To follow the established naming convention of other web/cookie providers (e.g., kimi-web, doubao-web, qwen-web), these should be renamed to huggingchat-web and phind-web. This prevents key collisions and resolution ambiguity.

Suggested change
huggingchat: {
id: "huggingchat",
// "hc" belongs to the hackclub provider; huggingchat uses its own id as alias.
alias: "huggingchat",
name: "HuggingChat (Free)",
icon: "auto_awesome",
color: "#FFD21E",
textIcon: "HC",
website: "https://huggingface.co/chat",
hasFree: true,
freeNote: "Free LLM chat — no subscription required. Rate limits apply.",
authHint:
"Paste your hf-chat cookie value from huggingface.co/chat (DevTools → Application → Cookies → hf-chat). Optional — works without auth for basic use.",
riskNoticeVariant: "webCookie",
},
"huggingchat-web": {
id: "huggingchat-web",
// "hc" belongs to the hackclub provider; huggingchat uses its own id as alias.
alias: "huggingchat",
name: "HuggingChat (Free)",
icon: "auto_awesome",
color: "#FFD21E",
textIcon: "HC",
website: "https://huggingface.co/chat",
hasFree: true,
freeNote: "Free LLM chat — no subscription required. Rate limits apply.",
authHint:
"Paste your hf-chat cookie value from huggingface.co/chat (DevTools → Application → Cookies → hf-chat). Optional — works without auth for basic use.",
riskNoticeVariant: "webCookie",
},

Comment on lines +187 to +200
phind: {
id: "phind",
alias: "ph",
name: "Phind (Free)",
icon: "auto_awesome",
color: "#000000",
textIcon: "PH",
website: "https://www.phind.com",
hasFree: true,
freeNote: "Free dev-focused AI chat with code search. Rate limits apply.",
authHint:
"Paste your session cookie from phind.com (DevTools → Application → Cookies). Optional — works with free tier.",
riskNoticeVariant: "webCookie",
},

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.

medium

Rename phind to phind-web to avoid duplicate key conflicts with APIKEY_PROVIDERS and maintain naming consistency.

Suggested change
phind: {
id: "phind",
alias: "ph",
name: "Phind (Free)",
icon: "auto_awesome",
color: "#000000",
textIcon: "PH",
website: "https://www.phind.com",
hasFree: true,
freeNote: "Free dev-focused AI chat with code search. Rate limits apply.",
authHint:
"Paste your session cookie from phind.com (DevTools → Application → Cookies). Optional — works with free tier.",
riskNoticeVariant: "webCookie",
},
"phind-web": {
id: "phind-web",
alias: "ph",
name: "Phind (Free)",
icon: "auto_awesome",
color: "#000000",
textIcon: "PH",
website: "https://www.phind.com",
hasFree: true,
freeNote: "Free dev-focused AI chat with code search. Rate limits apply.",
authHint:
"Paste your session cookie from phind.com (DevTools → Application → Cookies). Optional — works with free tier.",
riskNoticeVariant: "webCookie",
},

Comment on lines +37 to +53
export function providerAllowsOptionalApiKey(providerId: unknown): boolean {
return (
providerId === "searxng-search" ||
providerId === "pollinations" ||
providerId === "copilot-web" ||
providerId === "duckduckgo-web" ||
providerId === "veoaifree-web" ||
providerId === "hackclub" ||
providerId === "huggingchat" ||
providerId === "gitlawb" ||
providerId === "gitlawb-gmi" ||
isLocalProvider(providerId) ||
isSelfHostedChatProvider(providerId) ||
isOpenAICompatibleProvider(providerId) ||
isAnthropicCompatibleProvider(providerId)
);
}

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.

medium

If huggingchat and phind are renamed to huggingchat-web and phind-web in WEB_COOKIE_PROVIDERS, they should be added to providerAllowsOptionalApiKey to ensure they are correctly recognized as allowing optional/no auth.

export function providerAllowsOptionalApiKey(providerId: unknown): boolean {
  return (
    providerId === "searxng-search" ||
    providerId === "pollinations" ||
    providerId === "copilot-web" ||
    providerId === "duckduckgo-web" ||
    providerId === "veoaifree-web" ||
    providerId === "hackclub" ||
    providerId === "huggingchat" ||
    providerId === "huggingchat-web" ||
    providerId === "phind-web" ||
    providerId === "gitlawb" ||
    providerId === "gitlawb-gmi" ||
    isLocalProvider(providerId) ||
    isSelfHostedChatProvider(providerId) ||
    isOpenAICompatibleProvider(providerId) ||
    isAnthropicCompatibleProvider(providerId)
  );
}

@@ -0,0 +1,13 @@
// Re-export everything to maintain backward compatibility

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.

medium

According to the Repository Style Guide (Hard Rules, Rule 9):

Always include tests when changing production code (src/, open-sse/, electron/, bin/).

Since this PR refactors and modularizes the provider constants under src/shared/constants/providers/, please ensure that corresponding unit tests (e.g., verifying that getProviderById, getProviderByAlias, and the proxies resolve correctly) are added or updated in the tests/ directory.

References
  1. Always include tests when changing production code (src/, open-sse/, electron/, bin/). (link)

@diegosouzapw

Copy link
Copy Markdown
Owner

Thanks @oyi77 — blockers for the providers-constants modularization:

  1. Sub-split: definitions/apiKey.ts (1731) exceeds the 800-line new-file cap (Fast Quality Gates red). Split by provider category.
  2. Verify lossless — critical here. This is the canonical provider list. The registry sibling refactor: modularize provider registry (non-stacked) #3993 silently dropped byteplus + mimocode, so please diff the full provider set monolith-vs-modules (e.g. compare the exported provider-id list / getProviderIds() before vs after) and confirm zero provider definitions are lost, then rebase onto current release/v3.8.27.

@oyi77
oyi77 force-pushed the pr/providers branch 3 times, most recently from ba5e277 to ed357ca Compare June 17, 2026 05:21
oyi77 added a commit to oyi77/OmniRoute that referenced this pull request Jun 17, 2026
… tests

Address review feedback on diegosouzapw#3994:
- Align getOrCreateAliasToId and getOrCreateIdToAlias to first-one-wins strategy
- Rename huggingchat→huggingchat-web, phind→phind-web in WEB_COOKIE_PROVIDERS
- Add renamed providers to providerAllowsOptionalApiKey
- Add resolution strategy tests
@diegosouzapw
diegosouzapw changed the base branch from release/v3.8.27 to release/v3.8.28 June 17, 2026 08:19
oyi77 added a commit to oyi77/OmniRoute that referenced this pull request Jun 17, 2026
… tests

Address review feedback on diegosouzapw#3994:
- Align getOrCreateAliasToId and getOrCreateIdToAlias to first-one-wins strategy
- Rename huggingchat→huggingchat-web, phind→phind-web in WEB_COOKIE_PROVIDERS
- Add renamed providers to providerAllowsOptionalApiKey
- Add resolution strategy tests
oyi77 added 5 commits June 17, 2026 20:39
…zapw#3594)

- Splits the monolithic provider constants file into 12 domain-specific files
- Extracts provider dictionaries into definitions/ (apiKey, oauth, webCookie, local, search, etc.)
- Uses folder-as-module pattern with index.ts facade for zero backward-compatibility breakages
- Auto-prunes and manages all imports for strict typechecking
… tests

Address review feedback on diegosouzapw#3994:
- Align getOrCreateAliasToId and getOrCreateIdToAlias to first-one-wins strategy
- Rename huggingchat→huggingchat-web, phind→phind-web in WEB_COOKIE_PROVIDERS
- Add renamed providers to providerAllowsOptionalApiKey
- Add resolution strategy tests
…larization

The provider-consistency CI gate was failing because mimocode was missing
from the modularized providers/definitions/noauth.ts. Added it back with
all fields matching upstream release/v3.8.28.

Closes: check:provider-consistency failure
@diegosouzapw

Copy link
Copy Markdown
Owner

Thanks for all the modularization work here, @oyi77 🙏. We've decided to hold the per-module "non-stacked" refactors and run the decomposition as one coordinated pass after the in-flight quality-gate work lands, instead of merging them piecemeal. Reason: on the two we did merge (#3993, #3988) we caught logic being dropped during the move — and the gates (provider-consistency / typecheck) don't detect internal-logic loss — so each of these needs a full lossless audit, which isn't tractable across many overlapping PRs against a moving release branch right now. The coordinated modularization is tracked in #3501 / #3594; we'd genuinely value your input on that plan once it's up. Closing for now — purely sequencing, not a reflection on the effort.

oyi77 added a commit to oyi77/OmniRoute that referenced this pull request Jun 20, 2026
…egosouzapw#3994)

Split src/shared/constants/providers.ts (3241 lines) into 21 focused modules.
Sub-split oversized apiKey.ts (1731→6 sub-files).
Original file replaced with thin re-export.

Closes diegosouzapw#3594
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