Skip to content

Add Morpheus provider with dynamic model discovery#1488

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

Add Morpheus provider with dynamic model discovery#1488
betterbrand wants to merge 5 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 sensible defaults per provider.
    • UI improvements: loading indicators and user-facing error messages during model discovery; provider-specific plugin defaults applied.
  • 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

📝 Walkthrough

Walkthrough

Added new providers (morpheus, ionet, openrouter) across env, backend and frontend schemas, and updated the provider modal to apply provider-specific defaults and asynchronously fetch/display provider models with loading/error handling.

Changes

Cohort / File(s) Summary
Environment Configuration
.env.example
Added MORPHEUS_API_KEY placeholder and comment.
Backend Provider Schema
backend/node/create_nodes/providers_schema.json
Added ionet and morpheus to provider examples; added an allOf conditional for provider == "morpheus" setting x-models-url, x-plugin-config-defaults (MORPHEUS_API_KEY, api_url), and plugin: "openai-compatible".
Frontend Provider Schema
frontend/src/assets/schemas/providers_schema.json
Expanded provider enum to include openrouter, ionet, morpheus; added allOf blocks for openrouter, ionet, and morpheus to constrain plugin, allowed model values, and provide x-models-url / x-plugin-config-defaults for Morpheus.
Provider Modal Component
frontend/src/components/Simulator/ProviderModal.vue
Added isLoadingModels, modelFetchError, fetchDynamicModels to fetch/filter models from x-models-url, apply x-plugin-config-defaults when creating, and UI elements to show loading state and fetch errors.

Sequence Diagram

sequenceDiagram
    participant User
    participant ProviderModal
    participant SchemaProcessor
    participant MorpheusAPI
    participant UIState

    User->>ProviderModal: select provider (e.g., "morpheus")
    ProviderModal->>SchemaProcessor: evaluate provider schema
    SchemaProcessor-->>ProviderModal: return x-models-url & x-plugin-config-defaults
    ProviderModal->>UIState: set isLoadingModels = true
    ProviderModal->>MorpheusAPI: fetch models from x-models-url
    MorpheusAPI-->>ProviderModal: models list / error
    ProviderModal->>ProviderModal: filter LLMs, apply plugin_config_defaults
    ProviderModal->>UIState: populate modelOptions, set default model, set isLoadingModels = false / set modelFetchError
    UIState-->>User: display models or error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Suggested labels

run-tests

Poem

🐰 I found new providers in a hop,

Keys and models — I fetched from the top,
Defaults tucked in a cozy spot,
Loading lights and errors caught,
A little rabbit’s code-hop nonstop. 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding Morpheus as a provider with dynamic model discovery functionality.
Description check ✅ Passed The description provides comprehensive coverage of the What, Why, and How with clear file summaries, but lacks evidence of completed Testing, Decisions, and standard PR checklist items.
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
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

CodeRabbit can suggest fixes for GitHub Check annotations.

Configure the reviews.tools.github-checks setting to adjust the time to wait for GitHub Checks to complete.

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.

@betterbrand
Copy link
Author

@cristiam86 @kstroobants — the changes you requested should be reflected. Thanks!

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.

🧹 Nitpick comments (1)
backend/node/create_nodes/providers_schema.json (1)

221-239: Add a frontend/backend schema parity check to prevent future drift.

This new Morpheus conditional now exists in both schemas; adding a CI guard (snapshot/diff of provider condition blocks) would prevent recurrence of schema drift regressions.

🤖 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 221 - 239, Add
a CI parity check that snapshots and diffs provider conditional blocks between
the frontend and backend schemas and fails the build on any mismatch; implement
the check to locate conditional objects where if.properties.provider.const ===
"morpheus" (and likewise for other providers), and compare the entire then block
(e.g., x-models-url, x-plugin-config-defaults.api_key_env_var,
x-plugin-config-defaults.api_url, and properties.plugin.const ===
"openai-compatible") across repos, emitting a clear diff on failure; wire this
into existing CI test runner as a unit/integration test that loads both schema
JSONs, extracts provider condition blocks, and asserts deep-equality or updates
a committed snapshot.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@backend/node/create_nodes/providers_schema.json`:
- Around line 221-239: Add a CI parity check that snapshots and diffs provider
conditional blocks between the frontend and backend schemas and fails the build
on any mismatch; implement the check to locate conditional objects where
if.properties.provider.const === "morpheus" (and likewise for other providers),
and compare the entire then block (e.g., x-models-url,
x-plugin-config-defaults.api_key_env_var, x-plugin-config-defaults.api_url, and
properties.plugin.const === "openai-compatible") across repos, emitting a clear
diff on failure; wire this into existing CI test runner as a unit/integration
test that loads both schema JSONs, extracts provider condition blocks, and
asserts deep-equality or updates a committed snapshot.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5b71cff1-91a4-4624-adcb-5609cb6f5dba

📥 Commits

Reviewing files that changed from the base of the PR and between 1822e91 and 9086ad3.

📒 Files selected for processing (2)
  • .env.example
  • backend/node/create_nodes/providers_schema.json

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