Skip to content

Add Morpheus provider with dynamic model discovery#1488

Open
betterbrand wants to merge 4 commits intogenlayerlabs:mainfrom
betterbrand:feat/morpheus-provider
Open

Add Morpheus provider with dynamic model discovery#1488
betterbrand wants to merge 4 commits intogenlayerlabs:mainfrom
betterbrand:feat/morpheus-provider

Conversation

@betterbrand
Copy link

@betterbrand betterbrand commented Feb 26, 2026

With the integration of the Morpheus Marketplace, Intelligent Contracts gain censorship-resistant inference with no single point of failure, and the available model catalog updates automatically as new providers join the marketplace. This PR adds Morpheus as a provider in GenLayer Studio with fully dynamic model discovery. When a user selects Morpheus, Studio fetches the live model catalog from the Morpheus marketplace in real time, so validators always see whatever models are currently available across the decentralized network.

Summary

  • Add Morpheus to provider schemas (frontend + backend) with a new x-models-url extension for dynamic model fetching
  • Extend ProviderModal.vue to detect x-models-url and populate the model dropdown from the Morpheus API at runtime
  • Pre-fill Morpheus-specific defaults (MORPHEUSAPIKEY, API URL) via x-plugin-config-defaults schema extension
  • Add MORPHEUSAPIKEY to .env.example

How it works

The integration uses a schema-driven approach: a custom x-models-url JSON Schema property tells the frontend where to fetch models dynamically. This is generic — any future provider can opt in by adding x-models-url to their schema block. If the API is unreachable, the UI gracefully degrades to a free-text input.

Files changed

  • frontend/src/assets/schemas/providers_schema.json — add Morpheus conditional block with x-models-url
  • backend/node/create_nodes/providers_schema.json — same schema changes (backend mirror)
  • frontend/src/components/Simulator/ProviderModal.vue — dynamic model fetching, loading states, provider-specific defaults
  • .env.example — add MORPHEUSAPIKEY placeholder

Test plan

  • cd frontend && npm run test — all 113 unit tests pass
  • Select Morpheus in New Provider Preset — model dropdown populates with live models from API
  • api_key_env_var pre-fills as MORPHEUSAPIKEY, api_url as https://api.mor.org/api/v1
  • If API is unreachable, falls back to free-text model input with warning message
  • CORS verified: api.mor.org returns proper headers for browser requests

Summary by CodeRabbit

  • New Features

    • Added Morpheus, OpenRouter, and IONet as selectable AI model providers.
    • Dynamic model discovery with automatic model lists and defaults; provider-specific plugin defaults applied.
    • UI improvements: loading indicators and user-facing error messages during model discovery.
  • Chores

    • Added MORPHEUS_API_KEY placeholder to the environment example.

Add Morpheus decentralized AI inference as a provider option.
Models are fetched in real time from the Morpheus marketplace API
via a schema-driven x-models-url mechanism, with graceful fallback
to manual text input if the API is unreachable.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 739bf9a and 1822e91.

📒 Files selected for processing (3)
  • .env.example
  • backend/node/create_nodes/providers_schema.json
  • frontend/src/assets/schemas/providers_schema.json

📝 Walkthrough

Walkthrough

Added Morpheus, IONet, and OpenRouter as provider options: environment variable placeholder, backend and frontend schema entries with provider-specific defaults and models URL, and UI changes to fetch and display provider models dynamically with loading/error handling.

Changes

Cohort / File(s) Summary
Environment Configuration
.env.example
Added MORPHEUS_API_KEY placeholder entry.
Backend Provider Schema
backend/node/create_nodes/providers_schema.json
Added morpheus and ionet to provider examples and a new conditional allOf for provider == "morpheus" that sets x-models-url: https://api.mor.org/api/v1/models, x-plugin-config-defaults: { api_key_env_var: MORPHEUS_API_KEY, api_url: https://api.mor.org/api/v1 }, and plugin: "openai-compatible".
Frontend Provider Schema
frontend/src/assets/schemas/providers_schema.json
Extended provider enum to include openrouter, ionet, and morpheus; added allOf blocks for openrouter, ionet, and morpheus (provider-specific model enums, x-models-url, and x-plugin-config-defaults for Morpheus).
Provider Modal Component
frontend/src/components/Simulator/ProviderModal.vue
Added isLoadingModels and modelFetchError state, fetchDynamicModels async logic to load/filter models from x-models-url, apply x-plugin-config-defaults when creating, and UI elements for loading indicator and error display.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant Modal as ProviderModal
    participant Schema as Schema Processor
    participant API as Morpheus API
    participant UI as UI State

    User->>Modal: Select provider "morpheus"
    Modal->>Schema: Evaluate provider schema
    Schema-->>Modal: Return x-models-url & x-plugin-config-defaults
    Modal->>UI: isLoadingModels = true
    Modal->>API: fetchDynamicModels(x-models-url)
    API-->>Modal: models list
    Modal->>Modal: Filter LLM models & apply plugin_config defaults
    Modal->>UI: populate modelOptions & set default model
    Modal->>UI: isLoadingModels = false / show errors if any
    UI-->>User: Display models and provider options
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I hopped to add a MORPHEUS key,

Fetching models from the open sea,
Defaults tucked where they belong,
Errors shown if fetch goes wrong,
A rabbit cheers — new providers free! 🎉🐇

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding Morpheus as a provider with dynamic model discovery functionality.
Description check ✅ Passed The description comprehensively covers What, Why, and How with detailed file changes and test plan, though formal sections don't precisely match the template structure.
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
  • Post copyable unit tests in a comment

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/node/create_nodes/providers_schema.json (1)

77-83: ⚠️ Potential issue | 🟡 Minor

Duplicate model entry in google enum.

"gemini-2.5-flash-lite" appears twice in the enum (lines 79 and 82). This won't break functionality but is likely unintended.

🔧 Proposed fix
           "model": {
             "enum": [
               "gemini-2.0-flash-lite-001",
               "gemini-2.5-flash-lite",
               "gemini-2.5-flash",
-              "gemini-3.0-flash",
-              "gemini-2.5-flash-lite"
+              "gemini-3.0-flash"
             ]
           }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/node/create_nodes/providers_schema.json` around lines 77 - 83, Remove
the duplicate enum entry "gemini-2.5-flash-lite" from the providers_schema.json
models enum so each model appears only once; locate the "enum" array that lists
model names (contains "gemini-2.0-flash-lite", "gemini-2.5-flash-lite", etc.)
and delete the repeated "gemini-2.5-flash-lite" entry to ensure unique values.
🧹 Nitpick comments (1)
frontend/src/components/Simulator/ProviderModal.vue (1)

206-228: Consider adding timeout and request cancellation for reliability.

The dynamic model fetch lacks a timeout and cancellation mechanism. If the Morpheus API is slow or unresponsive, the request could hang indefinitely. Additionally, if the user switches providers while a fetch is in progress, stale results could populate the dropdown.

♻️ Suggested improvement with AbortController and timeout
+let modelFetchController: AbortController | null = null;
+
 const fetchDynamicModels = async (modelsUrl: string) => {
+  // Cancel any in-flight request
+  if (modelFetchController) {
+    modelFetchController.abort();
+  }
+  modelFetchController = new AbortController();
+
   isLoadingModels.value = true;
   modelFetchError.value = '';
   try {
-    const response = await fetch(modelsUrl);
+    const timeoutId = setTimeout(() => modelFetchController?.abort(), 10000);
+    const response = await fetch(modelsUrl, { signal: modelFetchController.signal });
+    clearTimeout(timeoutId);
     if (!response.ok) throw new Error(`HTTP ${response.status}`);
     const data = await response.json();
     // ... rest of the logic
   } catch (err) {
+    if ((err as Error).name === 'AbortError') return;
     console.error('Failed to fetch models:', err);
     modelFetchError.value =
       'Could not fetch available models. You can type a model name manually.';
   } finally {
     isLoadingModels.value = false;
+    modelFetchController = null;
   }
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/Simulator/ProviderModal.vue` around lines 206 - 228,
The fetchDynamicModels function should be made cancellable and time-limited:
create an AbortController for each call (store the controller in a scoped
variable so subsequent calls can abort the previous request), pass
controller.signal to fetch(modelsUrl), and implement a timeout (e.g., setTimeout
that calls controller.abort() after N ms) to ensure the request doesn't hang;
handle AbortError in the catch to avoid showing the generic modelFetchError for
intentional cancellations, and ensure the timeout is cleared and
isLoadingModels.value is reset in the finally block; update references to
modelOptions.value, newProviderData.model and modelFetchError.value exactly
where fetchDynamicModels sets them so stale responses cannot overwrite UI state
after an abort.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/assets/schemas/providers_schema.json`:
- Around line 14-16: The frontend providers schema is missing the "openrouter"
provider present in the backend examples; either add "openrouter" to the
providers list in frontend/src/assets/schemas/providers_schema.json and mirror
the necessary allOf configuration used in the backend (match the backend's
provider example and schema fields for "openrouter"), or add a clear JSON
comment/docstring in that same schema file explaining that "openrouter" is
backend-only and intentionally omitted; locate the providers array near the
entries "google" and "morpheus" and update it accordingly and ensure any
referenced schema definitions used by the backend's "openrouter" example are
also added or referenced.

---

Outside diff comments:
In `@backend/node/create_nodes/providers_schema.json`:
- Around line 77-83: Remove the duplicate enum entry "gemini-2.5-flash-lite"
from the providers_schema.json models enum so each model appears only once;
locate the "enum" array that lists model names (contains
"gemini-2.0-flash-lite", "gemini-2.5-flash-lite", etc.) and delete the repeated
"gemini-2.5-flash-lite" entry to ensure unique values.

---

Nitpick comments:
In `@frontend/src/components/Simulator/ProviderModal.vue`:
- Around line 206-228: The fetchDynamicModels function should be made
cancellable and time-limited: create an AbortController for each call (store the
controller in a scoped variable so subsequent calls can abort the previous
request), pass controller.signal to fetch(modelsUrl), and implement a timeout
(e.g., setTimeout that calls controller.abort() after N ms) to ensure the
request doesn't hang; handle AbortError in the catch to avoid showing the
generic modelFetchError for intentional cancellations, and ensure the timeout is
cleared and isLoadingModels.value is reset in the finally block; update
references to modelOptions.value, newProviderData.model and
modelFetchError.value exactly where fetchDynamicModels sets them so stale
responses cannot overwrite UI state after an abort.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebe224a and 0771521.

📒 Files selected for processing (4)
  • .env.example
  • backend/node/create_nodes/providers_schema.json
  • frontend/src/assets/schemas/providers_schema.json
  • frontend/src/components/Simulator/ProviderModal.vue

The backend schema included openrouter and ionet provider blocks
(added in genlayerlabs#1427/genlayerlabs#1429) but these were never mirrored to the frontend.
Sync the examples array and allOf conditional blocks so both schemas
list the same set of providers.
@betterbrand
Copy link
Author

betterbrand commented Feb 26, 2026

Good catch by CodeRabbit on the openrouter/ionet frontend schema drift. T

Addressed in 739bf9a where both schemas now list the same 9 providers with matching allOf blocks. Happy to help as always from the Morpheus community

@betterbrand
Copy link
Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@betterbrand
Copy link
Author

@cristiam86 @kstroobants — this is ready for review when you get a chance. Two commits:

  1. Morpheus provider with dynamic model discovery (no hardcoded models)
  2. Bonus: syncs openrouter and ionet to the frontend schema (pre-existing drift from chore: set openrouter presets for gpt5.1, sonnet 4.5, and gemini 3 flash #1427/fix: set operouter presets #1429)

Match the underscore convention used by newer providers
(ANTHROPIC_API_KEY, XAI_API_KEY, GEMINI_API_KEY).
@betterbrand
Copy link
Author

betterbrand commented Feb 28, 2026

Third commit renames the env var from MORPHEUSAPIKEY to MORPHEUS_API_KEY.

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