Skip to content

feat: model providers — 17 providers, dynamic model map, prompter#722

Open
Z0mb13V1 wants to merge 1 commit intomindcraft-bots:developfrom
Z0mb13V1:feat/pr3-model-providers
Open

feat: model providers — 17 providers, dynamic model map, prompter#722
Z0mb13V1 wants to merge 1 commit intomindcraft-bots:developfrom
Z0mb13V1:feat/pr3-model-providers

Conversation

@Z0mb13V1
Copy link

@Z0mb13V1 Z0mb13V1 commented Mar 4, 2026

Model Provider Updates

Adds 6 new providers and updates all existing ones. Replaces hard-coded model routing with a dynamic model-string auto-routing system.

New Providers

  • \grok.js\ — xAI Grok (Grok-2, Grok-3)
  • \cerebras.js\ — Cerebras Cloud
  • \mercury.js\ — Mercury AI

  • ovita.js\ — Novita AI
  • \hyperbolic.js\ — Hyperbolic Labs
  • \glhf.js\ — GLHF (free Hugging Face inference)

Core Changes

  • _model_map.js: Dynamic provider registry — model strings auto-route to the correct adapter ('gemini-2.5-pro'\ → GeminiAPI, 'grok-3'\ → GrokAPI)
  • \prompter.js: Refactored to use model map; cleaner multi-provider initialization
  • All providers: env-first API key loading via \keys.js\

Updated Providers

gemini, claude, gpt, ollama, openrouter, qwen, deepseek, vllm, huggingface

…ter refactor

- src/models/_model_map.js: dynamic provider discovery, string-based auto-routing
- New providers: grok.js, cerebras.js, mercury.js, novita.js, hyperbolic.js, glhf.js
- Updated: gemini.js, claude.js, gpt.js, ollama.js, openrouter.js, qwen.js
- Updated: prompter.js, deepseek.js, vllm.js, huggingface.js
- Providers support streamed and non-streamed responses, embeddings where available
- All providers load API keys via env-first keys.js
Copilot AI review requested due to automatic review settings March 4, 2026 01:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR expands the model-provider layer (now targeting 17 providers) by adding a dynamic provider/model routing map and refactoring Prompter to initialize models through that registry, with additional usage-tracking hooks and provider consistency updates.

Changes:

  • Introduces dynamic model-string → provider adapter routing via src/models/_model_map.js.
  • Refactors src/models/prompter.js to use the model map (and adds new prompt placeholder expansions + logging/usage hooks).
  • Updates multiple provider adapters to align with the new initialization/usage patterns (including usage capture in several adapters).

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/models/_model_map.js Adds a guard for missing model configuration during provider selection.
src/models/prompter.js Refactors model initialization to use model map; adds wiki/learnings placeholders; introduces usage tracking hooks and async profile save.
src/models/vllm.js Refactors vLLM config/init; adds retries, <think> stripping, usage capture, and vision request wrapper.
src/models/ollama.js Switches transport to node http/https; adds token usage capture; changes vision request formatting.
src/models/gemini.js Updates Google GenAI request shape and captures usage; makes embedding return compatible across response shapes.
src/models/grok.js Captures usage and hardens separator replacement against null responses.
src/models/gpt.js Renames an internal messages variable (currently leaving unused/dead code).
src/models/cerebras.js Adjusts unused parameter naming and embed signature.
src/models/openrouter.js Removes unused key-check import; marks unused embed parameter.
src/models/qwen.js Removes unused key-check import.
src/models/deepseek.js Removes unused key-check import; marks unused embed parameter.
src/models/mercury.js Removes unused key-check import.
src/models/claude.js Marks unused embed parameter.
src/models/huggingface.js Marks unused embed parameter.
src/models/novita.js Marks unused embed parameter.
src/models/hyperbolic.js Normalizes formatting; marks unused embed parameter.
src/models/glhf.js Normalizes formatting; marks unused embed parameter.
Comments suppressed due to low confidence (2)

src/models/ollama.js:57

  • res is assumed to be a string, but apiResponse['message']['content'] can be undefined (or this.send(...) can return null on error), which will make res.includes(...) throw. Coerce res to a string before checking <think> tags (or add a type guard) so the retry/fallback logic works reliably.
                });
                if (apiResponse) {
                    res = apiResponse['message']['content'];
                    this._lastUsage = {
                        prompt_tokens: apiResponse.prompt_eval_count || 0,
                        completion_tokens: apiResponse.eval_count || 0,
                        total_tokens: (apiResponse.prompt_eval_count || 0) + (apiResponse.eval_count || 0),
                    };
                } else {
                    res = 'No response data.';
                    this._lastUsage = null;
                }
            } catch (err) {
                if (err.message.toLowerCase().includes('context length') && turns.length > 1) {
                    console.log('Context length exceeded, trying again with shorter context.');
                    return await this.sendRequest(turns.slice(1), systemMessage);
                } else {
                    console.log(err);
                    res = 'My brain disconnected, try again.';
                }
            }

            const hasOpenTag = res.includes("<think>");
            const hasCloseTag = res.includes("</think>");

src/models/gpt.js:29

  • _messages is built from turns and has stop_seq appended, but it is never used (both branches later use turns/a new messages variable). This is dead code and makes it unclear which message set is intended to be sent; either use _messages in the responses-path or remove this block.
    async sendRequest(turns, systemMessage, stop_seq='***') {
        let _messages = strictFormat(turns);
        _messages = _messages.map(message => {
            message.content += stop_seq;
            return message;
        });

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +114 to 116
else if (typeof chat_model_profile !== 'undefined') {
this.embedding_model = createModel({api: chat_model_profile.api});
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

chat_model_profile is declared inside the else block, but later referenced when selecting a default embedding provider. Because it's block-scoped, the typeof chat_model_profile !== 'undefined' branch will never be taken, so embeddings will always fall back to api: 'google'. Declare chat_model_profile in the outer scope and assign it in the non-ensemble path (and decide what the embedding default should be for the ensemble path).

Suggested change
else if (typeof chat_model_profile !== 'undefined') {
this.embedding_model = createModel({api: chat_model_profile.api});
}

Copilot uses AI. Check for mistakes.
Comment on lines 74 to 78
// for backwards compatibility, move max_tokens to params
let max_tokens = null;
let _max_tokens = null;
if (this.profile.max_tokens)
max_tokens = this.profile.max_tokens;
_max_tokens = this.profile.max_tokens;

Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

The comment says max_tokens is moved into params for backwards compatibility, but _max_tokens is never used and this.profile.max_tokens is never applied to profile.params passed into createModel(). Either remove this dead code or actually merge max_tokens into this.profile.params (and consider deleting this.profile.max_tokens afterward to avoid ambiguity).

Copilot uses AI. Check for mistakes.
Comment on lines +63 to 66
// Handle <think> tags (Gemma-3 / reasoning models may produce these)
const hasOpenTag = res.includes('<think>');
const hasCloseTag = res.includes('</think>');

Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

res can be null/undefined (e.g., if completion.choices[0].message.content is missing) but the code calls res.includes(...), which will throw a TypeError and skip the retry/return logic. Coerce to a string before checking tags (e.g., res = res ?? '') or guard with typeof res === 'string'.

Copilot uses AI. Check for mistakes.
Comment on lines 7 to 9
import settings from '../agent/settings.js';
import { deepSanitize } from '../../settings.js';
import { promises as fs } from 'fs';
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

deepSanitize is imported as a named export from ../../settings.js, but settings.js only exports a default settings object (no deepSanitize). This will throw at module load time. Either export deepSanitize from settings.js, or move the sanitize helper into this module (or another util) and import it from there.

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +14
import { EnsembleModel } from '../ensemble/controller.js';
import { UsageTracker } from '../utils/usage_tracker.js';
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

Imports for EnsembleModel and UsageTracker point to modules that don't exist in this repo (src/ensemble/controller.js and src/utils/usage_tracker.js). This will fail at runtime when loading Prompter. Add these modules in the PR, or correct the import paths to the existing implementations.

Suggested change
import { EnsembleModel } from '../ensemble/controller.js';
import { UsageTracker } from '../utils/usage_tracker.js';

Copilot uses AI. Check for mistakes.
@Sweaterdog
Copy link
Contributor

Model Provider Updates

Adds 6 new providers and updates all existing ones. Replaces hard-coded model routing with a dynamic model-string auto-routing system.

New Providers

  • \grok.js\ — xAI Grok (Grok-2, Grok-3)
  • \cerebras.js\ — Cerebras Cloud
  • \mercury.js\ — Mercury AI

  • ovita.js\ — Novita AI
  • \hyperbolic.js\ — Hyperbolic Labs
  • \glhf.js\ — GLHF (free Hugging Face inference)

Core Changes

  • _model_map.js: Dynamic provider registry — model strings auto-route to the correct adapter ('gemini-2.5-pro'\ → GeminiAPI, 'grok-3'\ → GrokAPI)
  • \prompter.js: Refactored to use model map; cleaner multi-provider initialization
  • All providers: env-first API key loading via \keys.js\

Updated Providers

gemini, claude, gpt, ollama, openrouter, qwen, deepseek, vllm, huggingface

All of those providers already existed, GLHF has been rebranded though, it's not HF API though.

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.

3 participants