Skip to content

feat: add Astraflow (ModelVerse) provider support - #1481

Merged
zerob13 merged 2 commits into
ThinkInAIXYZ:devfrom
ucloudnb666:feat/astraflow-provider
Apr 17, 2026
Merged

feat: add Astraflow (ModelVerse) provider support#1481
zerob13 merged 2 commits into
ThinkInAIXYZ:devfrom
ucloudnb666:feat/astraflow-provider

Conversation

@ucloudnb666

@ucloudnb666 ucloudnb666 commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add Astraflow (Global) and Astraflow CN provider configs with correct API endpoints and website URLs
  • Register astraflow as a new modelSource strategy in providerRegistry.ts
  • Add case 'astraflow' in fetchProviderModelsByStrategy to filter out non-chat models (embedding, reranker, speech, tts, codex, whisper) from the model list

Background

Astraflow (formerly ModelVerse) is UCloud's AI model service that provides access to models including Claude, DeepSeek, GPT-4o, etc. The service offers two regional endpoints:

  • Global: https://api-us-ca.umodelverse.ai/v1 (US/Canada node)
  • CN: https://api.modelverse.cn/v1 (China node)

Why filter models?

The Astraflow /models endpoint returns all model types (chat, embedding, reranker, speech, image generation) without a type field to distinguish them. DeepChat's model list should only show chat-compatible models, so we apply keyword-based filtering for the astraflow provider.

Test plan

  • Configure Astraflow CN provider with a valid API key
  • Verify model list loads and shows only chat models (no embedding/reranker/speech)
  • Start a conversation with a chat model (e.g. claude-sonnet-4-6, deepseek-ai/DeepSeek-V3)
  • Verify responses stream correctly

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Introduced Astraflow as a new LLM provider with global and China region options
    • Integrated automatic model discovery with intelligent filtering for chat-compatible models
    • Added visual icon support for Astraflow provider identification

- Add Astraflow (Global) and Astraflow CN provider configs with correct
  API endpoints and website URLs
- Register astraflow modelSource strategy in providerRegistry
- Add astraflow case in fetchProviderModelsByStrategy to filter out
  non-chat models (embedding, reranker, speech, tts, etc.)
@coderabbitai

coderabbitai Bot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR adds support for two new Astraflow LLM providers (Global and CN variants) by registering them in the configuration, extending the provider registry with type definitions, implementing model fetching with filtering logic, and adding visual icon mappings.

Changes

Cohort / File(s) Summary
Provider Configuration
src/main/presenter/configPresenter/providers.ts
Added astraflow and astraflow-cn provider entries to DEFAULT_PROVIDERS with OpenAI-compatible API configuration, base URLs, and metadata websites.
Provider Registry
src/main/presenter/llmProviderPresenter/providerRegistry.ts
Extended AiSdkModelSourceStrategy union type to include 'astraflow' and registered both provider IDs in PROVIDER_ID_REGISTRY using OPENAI_BASE strategy.
Model Fetching Logic
src/main/presenter/llmProviderPresenter/providers/aiSdkProvider.ts
Implemented astraflow strategy in fetchProviderModelsByStrategy() to fetch OpenAI models and filter out non-chat models (embeddings, speech, codex, etc.) using substring blacklist pattern matching.
UI Icon Mapping
src/renderer/src/components/icons/ModelIcon.vue
Added astraflowIcon import and mapped model identifiers containing astraflow or astraflow-cn to the new icon asset.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested reviewers

  • zerob13

Poem

🐰 Two new paths through Astraflow gleam,
With filtering whiskers selecting the dream,
Icons dance as registries bloom,
New providers brightening every room! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding support for the Astraflow (ModelVerse) provider across configuration, registry, model fetching, and UI icon mapping.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ucloudnb666
ucloudnb666 marked this pull request as ready for review April 17, 2026 07:01

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (3)
src/main/presenter/llmProviderPresenter/providers/aiSdkProvider.ts (2)

916-931: Consider deduplicating with fetchDefaultOpenAIModels.

The mapping step here is identical to fetchDefaultOpenAIModels (Lines 572–586). You could collapse the astraflow branch to filter-then-delegate, e.g.:

case 'astraflow': {
  const models = await this.fetchDefaultOpenAIModels({ timeout: this.getModelFetchTimeout() })
  return models.filter(
    (m) => !ASTRAFLOW_NON_CHAT_PATTERNS.some((p) => m.id.toLowerCase().includes(p))
  )
}

Keeps mapping logic in one place and avoids drift if defaults change later.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/presenter/llmProviderPresenter/providers/aiSdkProvider.ts` around
lines 916 - 931, The astraflow branch duplicates the mapping logic from
fetchDefaultOpenAIModels; replace the inline filter+map with a call to
this.fetchDefaultOpenAIModels({ timeout: this.getModelFetchTimeout() }) and then
filter the returned models by the appropriate non-chat pattern constant (e.g.,
NON_CHAT_PATTERNS or ASTRAFLOW_NON_CHAT_PATTERNS) using
m.id.toLowerCase().includes(p) so all mapping remains in
fetchDefaultOpenAIModels and only filtering is applied in the astraflow case.

902-931: Hoist NON_CHAT_PATTERNS to a module-level constant and tighten the blacklist.

A few observations on the new astraflow branch:

  1. NON_CHAT_PATTERNS is allocated on every call to fetchProviderModelsByStrategy. Lift it to a module-level const (SCREAMING_SNAKE_CASE per coding guidelines) so it's instantiated once and easier to evolve.
  2. The -codex substring is broad — it will also drop any future codex-named chat/code variant that happens to contain -codex. Since Astraflow's /models returns no type field, a blacklist is pragmatic, but consider documenting the rationale or anchoring patterns (e.g., codex- prefix / /codex boundary) to minimize collateral filtering.
  3. uploads and suno- are unusual entries for a chat-filtering list; a brief inline comment with an example of the upstream id would help future maintainers.
♻️ Proposed refactor
+const ASTRAFLOW_NON_CHAT_PATTERNS = [
+  'embedding',
+  'reranker',
+  'speech',
+  'suno-',
+  'whisper',
+  '-codex',
+  'tts-',
+  'uploads'
+]
+
 // ...
       case 'astraflow': {
         const response = await this.fetchOpenAIModelRecords({
           timeout: this.getModelFetchTimeout()
         })
-        const NON_CHAT_PATTERNS = [
-          'embedding',
-          'reranker',
-          'speech',
-          'suno-',
-          'whisper',
-          '-codex',
-          'tts-',
-          'uploads'
-        ]
         return response
           .filter((model) => {
             if (typeof model.id !== 'string') return false
             const lower = model.id.toLowerCase()
-            return !NON_CHAT_PATTERNS.some((p) => lower.includes(p))
+            return !ASTRAFLOW_NON_CHAT_PATTERNS.some((p) => lower.includes(p))
           })

As per coding guidelines: "Constants must use SCREAMING_SNAKE_CASE naming".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/presenter/llmProviderPresenter/providers/aiSdkProvider.ts` around
lines 902 - 931, Hoist the NON_CHAT_PATTERNS array out of the astraflow case
into a module-level constant named NON_CHAT_MODEL_PATTERNS
(SCREAMING_SNAKE_CASE) so it’s allocated once and reusable by
fetchProviderModelsByStrategy; then tighten the blacklist entries (replace the
broad '-codex' with a more specific pattern such as 'codex-' or a word-boundary
style pattern, and prefer prefix/suffix anchoring instead of substring matches)
and add a short inline comment documenting why 'uploads' and 'suno-' are
filtered and showing an example upstream id; update the astraflow branch to
reference the new constant (NON_CHAT_MODEL_PATTERNS) when filtering models.
src/renderer/src/components/icons/ModelIcon.vue (1)

167-168: Redundant astraflow-cn entry in icons map.

The icon lookup at Line 197-199 uses modelIdLower.includes(key) and iterates Object.keys(icons) in insertion order. Since astraflow is registered before astraflow-cn and is a substring of it, astraflow-cn will never be matched — it's effectively dead. Both map to the same icon, so this is harmless, but consider dropping the duplicate entry for clarity.

♻️ Suggested cleanup
   astraflow: astraflowIcon,
-  'astraflow-cn': astraflowIcon,
   default: defaultIcon
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/src/components/icons/ModelIcon.vue` around lines 167 - 168,
Remove the redundant 'astraflow-cn' entry from the icons map in ModelIcon.vue:
the icons object currently contains both astraflow and 'astraflow-cn', but the
lookup logic (modelIdLower.includes(key) iterating Object.keys(icons)) will
always match 'astraflow' first and never reach 'astraflow-cn'; delete the
'astraflow-cn' key (leaving astraflow: astraflowIcon) to eliminate the dead
entry and keep the icons map unambiguous.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/main/presenter/llmProviderPresenter/providers/aiSdkProvider.ts`:
- Around line 916-931: The astraflow branch duplicates the mapping logic from
fetchDefaultOpenAIModels; replace the inline filter+map with a call to
this.fetchDefaultOpenAIModels({ timeout: this.getModelFetchTimeout() }) and then
filter the returned models by the appropriate non-chat pattern constant (e.g.,
NON_CHAT_PATTERNS or ASTRAFLOW_NON_CHAT_PATTERNS) using
m.id.toLowerCase().includes(p) so all mapping remains in
fetchDefaultOpenAIModels and only filtering is applied in the astraflow case.
- Around line 902-931: Hoist the NON_CHAT_PATTERNS array out of the astraflow
case into a module-level constant named NON_CHAT_MODEL_PATTERNS
(SCREAMING_SNAKE_CASE) so it’s allocated once and reusable by
fetchProviderModelsByStrategy; then tighten the blacklist entries (replace the
broad '-codex' with a more specific pattern such as 'codex-' or a word-boundary
style pattern, and prefer prefix/suffix anchoring instead of substring matches)
and add a short inline comment documenting why 'uploads' and 'suno-' are
filtered and showing an example upstream id; update the astraflow branch to
reference the new constant (NON_CHAT_MODEL_PATTERNS) when filtering models.

In `@src/renderer/src/components/icons/ModelIcon.vue`:
- Around line 167-168: Remove the redundant 'astraflow-cn' entry from the icons
map in ModelIcon.vue: the icons object currently contains both astraflow and
'astraflow-cn', but the lookup logic (modelIdLower.includes(key) iterating
Object.keys(icons)) will always match 'astraflow' first and never reach
'astraflow-cn'; delete the 'astraflow-cn' key (leaving astraflow: astraflowIcon)
to eliminate the dead entry and keep the icons map unambiguous.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 42774207-ec91-41a4-ba17-60d3c2c369f3

📥 Commits

Reviewing files that changed from the base of the PR and between 66687f2 and b3a7320.

⛔ Files ignored due to path filters (2)
  • src/renderer/src/assets/llm-icons/astraflow.png is excluded by !**/*.png
  • src/renderer/src/assets/llm-icons/astraflow.svg is excluded by !**/*.svg
📒 Files selected for processing (4)
  • src/main/presenter/configPresenter/providers.ts
  • src/main/presenter/llmProviderPresenter/providerRegistry.ts
  • src/main/presenter/llmProviderPresenter/providers/aiSdkProvider.ts
  • src/renderer/src/components/icons/ModelIcon.vue

@zerob13

zerob13 commented Apr 17, 2026

Copy link
Copy Markdown
Collaborator

LGTM

@zerob13
zerob13 merged commit 8f0d2d2 into ThinkInAIXYZ:dev Apr 17, 2026
3 checks passed
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