Skip to content

Enhance model profile handling in backend configuration and runtime.#197

Merged
jbax1899 merged 9 commits intomainfrom
model-profile-catalog-foundation
Mar 26, 2026
Merged

Enhance model profile handling in backend configuration and runtime.#197
jbax1899 merged 9 commits intomainfrom
model-profile-catalog-foundation

Conversation

@jbax1899
Copy link
Member

@jbax1899 jbax1899 commented Mar 26, 2026

Introduce optional model profile catalog path and default profile ID in environment settings. Update chat service and orchestrator to utilize model profiles for provider-specific routing and capabilities.

Summary by CodeRabbit

  • New Features

    • Backend model profile catalog for runtime-driven model routing (provider, capabilities, tiers)
    • New env vars to configure catalog path and default profile
    • Chat now resolves and applies profile (provider/providerModel/capabilities) to generation
    • Discord conversation normalization and surface-aware plan coercion
  • Contracts

    • Added shared model-profile types and validation schemas for catalog entries
  • Tests

    • Added tests covering catalog loading, selector resolution, and orchestration behavior

Introduce optional model profile catalog path and default profile ID in environment settings.
Update chat service and orchestrator to utilize model profiles for provider-specific routing and capabilities.
@jbax1899 jbax1899 self-assigned this Mar 26, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

Backend model-profile catalog and resolver added (YAML, env override, default selection). GenerationRequest gains optional provider and capabilities; agent/runtime routing updated to prefix provider where appropriate and gate search forwarding. New backend services for profile resolution, Discord normalization, profile overlays, and surface coercion. Tests and contracts added/updated.

Changes

Cohort / File(s) Summary
Env & Config
\.env.example, packages/config-spec/src/env-spec.ts, packages/backend/src/config/types.ts, packages/backend/src/config/buildRuntimeConfig.ts, packages/backend/src/config/model-profiles.defaults.yaml, packages/backend/src/config/sections/modelProfiles.ts
Added MODEL_PROFILE_CATALOG_PATH and DEFAULT_PROFILE_ID env entries; runtime config now includes modelProfiles (defaultProfileId, catalogPath, catalog). Loads/validates YAML catalog with project-root resolution and bundled fallback; emits warnings on issues.
Contracts
packages/contracts/src/model-profiles.ts, packages/contracts/src/index.ts
New model-profile contract types, enums, and Zod schemas (ModelProfile, ModelProfileCapabilities, tier/cost/latency aliases) and re-exports from contracts index.
Agent Runtime
packages/agent-runtime/src/index.ts, packages/agent-runtime/src/voltagentRuntime.ts, packages/agent-runtime/test/voltagentRuntime.test.ts
GenerationRequest adds optional provider and capabilities; model IDs are provider-prefixed when appropriate; search/tool forwarding gated by provider support and capabilities; tests updated/added for routing and search gating.
Backend Model Routing
packages/backend/src/services/modelProfileResolver.ts, packages/backend/test/modelProfileCatalog.test.ts
New resolver factory createModelProfileResolver maps selectors (profile id, tier alias, raw model, default) to ModelProfile, validates catalog, synthesizes passthrough/legacy profiles, and warns on ambiguous/invalid selectors; comprehensive tests added.
Chat Orchestration & Service
packages/backend/src/services/chatOrchestrator.ts, packages/backend/src/services/chatService.ts, packages/backend/src/handlers/chat.ts, packages/backend/test/chatOrchestrator.test.ts, packages/backend/test/chatService.test.ts
Orchestrator uses modelProfiles.defaultProfileId; resolves profile at startup and surfaces provider, providerModel, and capabilities to planner/runtime. Chat service accepts/forwards optional defaultProvider/defaultCapabilities and per-request provider/capabilities.
Discord & Surface Helpers
packages/backend/src/services/chatConversationNormalization.ts, packages/backend/src/services/chatProfileOverlay.ts, packages/backend/src/services/chatSurfacePolicy.ts
Added Discord conversation normalization (trimming, timestamp/author formatting), bot profile display-name and overlay resolution (with warnings for mismatched hints), and web-surface plan coercion to force text-generation for non-message actions.
Dependencies
packages/backend/package.json
Added js-yaml dependency for YAML catalog parsing.
Tests
packages/contracts/test/modelProfiles.test.ts, packages/backend/test/modelProfileCatalog.test.ts, packages/agent-runtime/test/voltagentRuntime.test.ts, packages/backend/test/chatOrchestrator.test.ts, packages/backend/test/chatService.test.ts
Added and updated tests covering contract schema validation, catalog loading/resolution, model routing/provider handling, search gating, and planner→runtime propagation.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Orchestrator as Chat Orchestrator
    participant Resolver as Model Profile Resolver
    participant Planner as Chat Planner
    participant Runtime as Generation Runtime
    participant VoltAgent as VoltAgent

    Client->>Orchestrator: POST /chat (selector or model)
    Orchestrator->>Resolver: resolve(selector)
    Resolver->>Resolver: lookup by id / tier / provider+model<br/>or synthesize passthrough/legacy
    Resolver-->>Orchestrator: ModelProfile (provider, providerModel, capabilities)

    Orchestrator->>Planner: execute(provider, providerModel, capabilities)
    Planner->>Runtime: generate(model, provider, capabilities, search?)
    Runtime->>VoltAgent: generateText(provider-prefixed model, search if supported)
    VoltAgent-->>Runtime: generation result
    Runtime-->>Planner: normalized result
    Planner-->>Orchestrator: plan
    Orchestrator->>Orchestrator: coerce plan for surface (web policy)
    Orchestrator-->>Client: response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement

🐰
A catalog of profiles hops into place,
Providers stitched with model and grace,
Discord threads trimmed and neat,
Surface rules make messages sweet,
Resolver guides routing’s race.

🚥 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 describes the main change: enhancing model profile handling across backend configuration and runtime layers.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch model-profile-catalog-foundation

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.

coderabbitai[bot]

This comment was marked as resolved.

…od to accept metadata and improving author label handling for better clarity in message formatting.
…nings for better debugging of profile ID mismatches.
… existing plan properties for improved plan handling.
… raw model. Default to 'openai' if provider is not specified, improving robustness in profile resolution logic.
…, enhancing flexibility in request handling. Adjust warning message for profile ID mismatches to improve clarity in error reporting.
…file ID handling, ensuring clear error reporting for data integrity.
coderabbitai[bot]

This comment was marked as resolved.

…ucing a structured warning format. Update the warning emission logic to include metadata for better context in logs. Adjust tests to validate new warning structure and ensure proper fallback behavior for model profile resolution.
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 (2)
packages/backend/src/services/modelProfileResolver.ts (2)

50-52: Return a fresh capabilities object for synthetic profiles.

RAW_MODEL_PROFILE_CAPABILITIES is reused by reference. If any downstream code mutates profile.capabilities, that mutation can leak into other synthetic profiles.

Proposed tweak
-const RAW_MODEL_PROFILE_CAPABILITIES: ModelProfile['capabilities'] = {
+const RAW_MODEL_PROFILE_CAPABILITIES: ModelProfile['capabilities'] = Object.freeze({
     canUseSearch: false,
-};
+});

 ...
-        capabilities: RAW_MODEL_PROFILE_CAPABILITIES,
+        capabilities: { ...RAW_MODEL_PROFILE_CAPABILITIES },

Also applies to: 148-148

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

In `@packages/backend/src/services/modelProfileResolver.ts` around lines 50 - 52,
RAW_MODEL_PROFILE_CAPABILITIES is a shared object reused by reference, so
mutations to profile.capabilities can leak across synthetic profiles; change
code that assigns RAW_MODEL_PROFILE_CAPABILITIES to each synthetic profile
(where profile.capabilities is set) to return a fresh object instead (e.g.,
clone via {...RAW_MODEL_PROFILE_CAPABILITIES} or a small factory function like
getDefaultCapabilities()), and update any places that construct
ModelProfile['capabilities'] to use that clone/factory so each profile gets its
own independent capabilities object.

178-194: Avoid “not found” warnings when DEFAULT_PROFILE_ID is intentionally unset.

Line 187 currently warns on missing profile even when the default profile ID is omitted by design. For optional config, that creates noisy startup warnings in valid setups.

Proposed tweak
 const resolveDefaultProfile = (): ModelProfile => {
-    const configuredDefault = catalogById.get(defaultProfileId);
+    const hasConfiguredDefaultId = defaultProfileId.trim().length > 0;
+    const configuredDefault = hasConfiguredDefaultId
+        ? catalogById.get(defaultProfileId)
+        : undefined;
     if (configuredDefault?.enabled) {
         return configuredDefault;
     }

     if (configuredDefault && !configuredDefault.enabled) {
         emitWarning(warn, {
             message:
                 'Configured DEFAULT_PROFILE_ID is disabled. Falling back to first enabled catalog profile.',
             meta: {
                 defaultProfileId,
             },
         });
-    } else {
+    } else if (hasConfiguredDefaultId) {
         emitWarning(warn, {
             message:
                 'Configured DEFAULT_PROFILE_ID was not found. Falling back to first enabled catalog profile.',
             meta: {
                 defaultProfileId,
             },
         });
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/backend/src/services/modelProfileResolver.ts` around lines 178 -
194, The current else branch emits a "not found" warning even when
DEFAULT_PROFILE_ID is intentionally unset; update the logic in
modelProfileResolver.ts around the configuredDefault check (references:
configuredDefault, defaultProfileId, emitWarning, warn) so that you only call
emitWarning about a missing profile when defaultProfileId is non-empty/defined
(e.g., truthy), and skip emitting any warning when the config value is
intentionally omitted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/backend/src/services/modelProfileResolver.ts`:
- Around line 50-52: RAW_MODEL_PROFILE_CAPABILITIES is a shared object reused by
reference, so mutations to profile.capabilities can leak across synthetic
profiles; change code that assigns RAW_MODEL_PROFILE_CAPABILITIES to each
synthetic profile (where profile.capabilities is set) to return a fresh object
instead (e.g., clone via {...RAW_MODEL_PROFILE_CAPABILITIES} or a small factory
function like getDefaultCapabilities()), and update any places that construct
ModelProfile['capabilities'] to use that clone/factory so each profile gets its
own independent capabilities object.
- Around line 178-194: The current else branch emits a "not found" warning even
when DEFAULT_PROFILE_ID is intentionally unset; update the logic in
modelProfileResolver.ts around the configuredDefault check (references:
configuredDefault, defaultProfileId, emitWarning, warn) so that you only call
emitWarning about a missing profile when defaultProfileId is non-empty/defined
(e.g., truthy), and skip emitting any warning when the config value is
intentionally omitted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 81b03dd1-b8c9-421e-ab8b-16032502d9ec

📥 Commits

Reviewing files that changed from the base of the PR and between 828a2c7 and 1a01934.

📒 Files selected for processing (3)
  • packages/backend/src/services/chatOrchestrator.ts
  • packages/backend/src/services/modelProfileResolver.ts
  • packages/backend/test/modelProfileCatalog.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/backend/test/modelProfileCatalog.test.ts
  • packages/backend/src/services/chatOrchestrator.ts

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.

1 participant