Skip to content

Conversation

@nettee
Copy link
Contributor

@nettee nettee commented Dec 22, 2025

Summary

This PR implements a rule-based auto-model routing system. It allows the system to dynamically select the best LLM model based on rules stored in the database, while keeping the existing environment-based fallbacks.

Changes

  • Added AutoModelRoutingRule and AutoModelRoutingResult tables to Prisma
  • Implemented AutoModelRoutingService with a 3-minute in-memory cache for routing rules
  • Added getModelSceneFromMode() helper to consistently map agent modes to scenes
  • Updated SkillService to pass more context (toolsets, input prompt) for smarter routing
  • Added logic to record routing decisions in the database for better observability
  • Updated agent templates to correctly use the agent scene for node_agent mode

Summary by CodeRabbit

  • New Features

    • Rule-based automatic model selection with multi-tier fallbacks (rule, tool, random, priority, first-available).
    • Persistent tracking/auditing of routing decisions with generated routing and rule IDs.
    • Consistent scene derivation from agent mode to improve model selection behavior.
  • Refactor

    • Centralized routing service with cached rule evaluation and asynchronous result persistence for more reliable, configurable routing.

✏️ Tip: You can customize this high-level summary in your review settings.

@nettee nettee requested a review from mrcfps December 22, 2025 06:43
@linear
Copy link

linear bot commented Dec 22, 2025

@coderabbitai
Copy link

coderabbitai bot commented Dec 22, 2025

Walkthrough

Adds Prisma models for auto-model routing and a new AutoModelRoutingService with rule-based routing, caching, multi-tier fallback selection, async routing-result persistence, integrations into ProviderModule and SkillService, and supporting ID/scene utilities.

Changes

Cohort / File(s) Change Summary
Prisma schema
apps/api/prisma/schema.prisma
Added AutoModelRoutingRule and AutoModelRoutingResult models with fields, indexes, unique constraints, timestamps, and table mappings (auto_model_routing_rules, auto_model_routing_results).
Routing service & helpers
apps/api/src/modules/provider/auto-model-router.service.ts
Added AutoModelRoutingService, RoutingContext and related types/enums (RuleCondition, RoutingTarget, RuleRouteResult, RoutingStrategy), internal RuleRouter and RuleCache, TTL-based caching with refresh and OnModuleDestroy cleanup, multi-tier routing pipeline (rule → tool → random → built-in priority → fallback), and sync/async routing-result persistence (saveRoutingResult*).
Provider module
apps/api/src/modules/provider/provider.module.ts
Registered and exported AutoModelRoutingService in ProviderModule providers/exports.
Skill integration
apps/api/src/modules/skill/skill.service.ts
Replaced previous router with AutoModelRoutingService; builds RoutingContext, routes only the primary scene, injects routedData into providerItem.config, preserves surface vs actual provider item IDs, and updates routing-related flows and validations.
ID utilities
packages/utils/src/id.ts
Added IDPrefix.ROUTING_RESULT and IDPrefix.ROUTING_RULE and generators genRoutingResultID() and genRoutingRuleID().
Model scene mapping
packages/utils/src/models.ts
Added getModelSceneFromMode(mode?) mapping (copilot_agentcopilot, node_agentagent, default→chat).
Skill template
packages/skill-template/src/skills/agent.ts
Replaced ad-hoc scene selection with getModelSceneFromMode(mode) in preprocess and component initialization.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant SkillService
    participant AutoModelRoutingService
    participant RuleCache
    participant RuleRouter
    participant PrismaService
    participant ProviderRegistry

    Client->>SkillService: invoke skill (mode, user, actionResult)
    SkillService->>AutoModelRoutingService: route(originalProviderItem, RoutingContext)

    AutoModelRoutingService->>RuleCache: getRules(scene)
    alt cache hit
        RuleCache-->>AutoModelRoutingService: rules
    else cache miss / stale
        RuleCache->>PrismaService: query AutoModelRoutingRule
        PrismaService-->>RuleCache: rules
        RuleCache-->>AutoModelRoutingService: rules
    end

    alt rule matches
        AutoModelRoutingService->>RuleRouter: evaluate(rules, context)
        RuleRouter-->>AutoModelRoutingService: matchedTarget
        AutoModelRoutingService->>ProviderRegistry: selectProviderItem(matchedTarget)
        ProviderRegistry-->>AutoModelRoutingService: providerItem
        AutoModelRoutingService->>PrismaService: saveRoutingResultAsync(...)
    else no match -> fallbacks
        AutoModelRoutingService->>ProviderRegistry: select via tool/random/priority/first
        ProviderRegistry-->>AutoModelRoutingService: providerItem
        AutoModelRoutingService->>PrismaService: saveRoutingResultAsync(...)
    end

    AutoModelRoutingService-->>SkillService: routed providerItem (with routedData)
    SkillService-->>Client: proceed with routed model
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Inspect routing priority/fallback logic and edge cases in auto-model-router.service.ts.
  • Review RuleCache TTL, refresh/concurrency, and OnModuleDestroy cleanup.
  • Validate Prisma schema indexes, unique constraints, and timestamp mappings.
  • Verify non-blocking persistence (saveRoutingResultAsync) and ID generation usage.
  • Confirm SkillService integration preserves surface IDs and injects routedData safely.

Possibly related PRs

Suggested reviewers

  • mrcfps

"🐇 I hopped through rules and cached each beat,
I nudged models gently toward their seat,
Five paths I know when choices are few,
I tag each hop — rrt- and rrl- too,
A merry rabbit cheering routing anew!"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main feature being implemented: rule-based auto model routing, as evidenced by the addition of routing models, service, and decision tracking throughout the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/920/auto-model-routing-rule

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 39dc7d9 and eefb838.

📒 Files selected for processing (7)
  • apps/api/prisma/schema.prisma
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/skill/skill.service.ts
  • packages/skill-template/src/skills/agent.ts
  • packages/utils/src/id.ts
  • packages/utils/src/models.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/skill-template/src/skills/agent.ts
  • packages/utils/src/id.ts
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{js,ts,jsx,tsx}: Always use optional chaining (?.) when accessing object properties
Always use nullish coalescing (??) or default values for potentially undefined values
Always check array existence before using array methods
Always validate object properties before destructuring
Always use single quotes for string literals in JavaScript/TypeScript code

**/*.{js,ts,jsx,tsx}: Use semicolons at the end of statements
Include spaces around operators (e.g., a + b instead of a+b)
Always use curly braces for control statements
Place opening braces on the same line as their statement

**/*.{js,ts,jsx,tsx}: Group import statements in order: React/framework libraries, third-party libraries, internal modules, relative path imports, type imports, style imports
Sort imports alphabetically within each import group
Leave a blank line between import groups
Extract complex logic into custom hooks
Use functional updates for state (e.g., setCount(prev => prev + 1))
Split complex state into multiple state variables rather than single large objects
Use useReducer for complex state logic instead of multiple useState calls

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{js,ts,tsx,jsx,py,java,cpp,c,cs,rb,go,rs,php,swift,kt,scala,r,m,mm,sql}

📄 CodeRabbit inference engine (.cursor/rules/00-language-priority.mdc)

**/*.{js,ts,tsx,jsx,py,java,cpp,c,cs,rb,go,rs,php,swift,kt,scala,r,m,mm,sql}: All code comments MUST be written in English
All variable names, function names, class names, and other identifiers MUST use English words
Comments should be concise and explain 'why' rather than 'what'
Use proper grammar and punctuation in comments
Keep comments up-to-date when code changes
Document complex logic, edge cases, and important implementation details
Use clear, descriptive names that indicate purpose
Avoid abbreviations unless they are universally understood

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/00-language-priority.mdc)

Use JSDoc style comments for functions and classes in JavaScript/TypeScript

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/01-code-style.mdc)

**/*.{js,jsx,ts,tsx}: Use single quotes for string literals in TypeScript/JavaScript
Always use optional chaining (?.) when accessing object properties in TypeScript/JavaScript
Always use nullish coalescing (??) or default values for potentially undefined values in TypeScript/JavaScript
Always check array existence before using array methods in TypeScript/JavaScript
Validate object properties before destructuring in TypeScript/JavaScript
Use ES6+ features like arrow functions, destructuring, and spread operators in TypeScript/JavaScript
Avoid magic numbers and strings - use named constants in TypeScript/JavaScript
Use async/await instead of raw promises for asynchronous code in TypeScript/JavaScript

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/03-typescript-guidelines.mdc)

**/*.{ts,tsx}: Avoid using any type whenever possible - use unknown type instead with proper type guards
Always define explicit return types for functions, especially for public APIs
Prefer extending existing types over creating entirely new types
Use TypeScript utility types (Partial<T>, Pick<T, K>, Omit<T, K>, Readonly<T>, Record<K, T>) to derive new types
Use union types and intersection types to combine existing types
Always import types explicitly using the import type syntax
Group type imports separately from value imports
Minimize creating local type aliases for imported types

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{js,ts,jsx,tsx,css,json}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

Maximum line length of 100 characters

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{js,ts,jsx,tsx,css,json,yml,yaml}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

Use 2 spaces for indentation, no tabs

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{js,ts,jsx,tsx,css,json,yml,yaml,md}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

No trailing whitespace at the end of lines

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{css,scss,sass,less,js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/09-design-system.mdc)

**/*.{css,scss,sass,less,js,jsx,ts,tsx}: Primary color (#155EEF) should be used for main brand color in buttons, links, and accents
Error color (#F04438) should be used for error states and destructive actions
Success color (#12B76A) should be used for success states and confirmations
Warning color (#F79009) should be used for warnings and important notifications
Info color (#0BA5EC) should be used for informational elements

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)

**/*.{tsx,ts}: Use the translation wrapper component and useTranslation hook in components
Ensure all user-facing text is translatable

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{tsx,ts,json}

📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)

Support dynamic content with placeholders in translations

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{tsx,ts,jsx,js,vue,css,scss,less}

📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)

**/*.{tsx,ts,jsx,js,vue,css,scss,less}: Use the primary blue (#155EEF) for main UI elements, CTAs, and active states
Use red (#F04438) only for errors, warnings, and destructive actions
Use green (#12B76A) for success states and confirmations
Use orange (#F79009) for warning states and important notifications
Use blue (#0BA5EC) for informational elements
Primary buttons should be solid with the primary color
Secondary buttons should have a border with transparent or light background
Danger buttons should use the error color
Use consistent padding, border radius, and hover states for all buttons
Follow fixed button sizes based on their importance and context
Use consistent border radius (rounded-lg) for all cards
Apply light shadows (shadow-sm) for card elevation
Maintain consistent padding inside cards (p-4 or p-6)
Use subtle borders for card separation
Ensure proper spacing between card elements
Apply consistent styling to all form inputs
Use clear visual indicators for focus, hover, and error states in form elements
Apply proper spacing between elements using 8px, 16px, 24px increments
Ensure proper alignment of elements (left, center, or right)
Use responsive layouts that work across different device sizes
Maintain a minimum contrast ratio of 4.5:1 for text

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{tsx,ts,jsx,js,vue}

📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)

**/*.{tsx,ts,jsx,js,vue}: Include appropriate loading states for async actions in buttons
Group related form elements with appropriate spacing
Provide clear validation feedback for forms
Ensure proper labeling and accessibility for form elements
Ensure all interactive elements are keyboard accessible
Include appropriate ARIA attributes for complex components
Provide alternative text for images and icons
Support screen readers with semantic HTML elements

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/08-contributing-guidelines.mdc)

**/*.{ts,tsx,js,jsx}: Follow the TypeScript/JavaScript style guidelines
Ensure code is well-tested and documented

Files:

  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
apps/api/src/**/*.{controller,service}.ts

📄 CodeRabbit inference engine (.cursor/rules/06-api-structure.mdc)

Implement proper error handling in API modules

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: nettee
Repo: refly-ai/refly PR: 1911
File: apps/api/src/modules/provider/auto-model-router.service.ts:545-559
Timestamp: 2025-12-22T09:48:22.120Z
Learning: In apps/api/src/modules/provider/auto-model-router.service.ts, reasoning models (where config.capabilities.reasoning === true) are intentionally excluded from auto-routing in the buildModelMap method. This is a deliberate design decision.
📚 Learning: 2025-12-22T09:48:22.120Z
Learnt from: nettee
Repo: refly-ai/refly PR: 1911
File: apps/api/src/modules/provider/auto-model-router.service.ts:545-559
Timestamp: 2025-12-22T09:48:22.120Z
Learning: In apps/api/src/modules/provider/auto-model-router.service.ts, reasoning models (where config.capabilities.reasoning === true) are intentionally excluded from auto-routing in the buildModelMap method. This is a deliberate design decision.

Applied to files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/prisma/schema.prisma
📚 Learning: 2025-11-25T03:04:12.836Z
Learnt from: CR
Repo: refly-ai/refly PR: 0
File: .cursor/rules/06-api-structure.mdc:0-0
Timestamp: 2025-11-25T03:04:12.836Z
Learning: Applies to apps/api/src/{auth,user,project,canvas,rag,knowledge,search,skill,share,code-artifact}/**/*.{controller,service}.ts : Use dependency injection for module dependencies in NestJS

Applied to files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
📚 Learning: 2025-11-25T03:04:12.836Z
Learnt from: CR
Repo: refly-ai/refly PR: 0
File: .cursor/rules/06-api-structure.mdc:0-0
Timestamp: 2025-11-25T03:04:12.836Z
Learning: Applies to apps/api/src/{auth,user,project,canvas,rag,knowledge,search,skill,share,code-artifact}/**/*.{ts,tsx} : Follow NestJS module structure with controllers, services, and DTOs in dedicated module directories

Applied to files:

  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
🧬 Code graph analysis (2)
packages/utils/src/models.ts (1)
packages/openapi-schema/src/types.gen.ts (2)
  • AgentMode (4177-4177)
  • ModelScene (2200-2208)
apps/api/src/modules/skill/skill.service.ts (2)
apps/api/src/modules/provider/auto-model-router.service.ts (1)
  • RoutingContext (60-97)
packages/utils/src/models.ts (1)
  • getModelSceneFromMode (10-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build / Build
🔇 Additional comments (14)
packages/utils/src/models.ts (1)

5-14: LGTM! Clean implementation of mode-to-scene mapping.

The function correctly maps agent modes to model scenes with explicit equality checks and proper type annotations. The JSDoc documentation clearly describes the mapping behavior.

apps/api/prisma/schema.prisma (2)

2186-2214: LGTM! Well-designed routing rule schema.

The AutoModelRoutingRule model has:

  • Proper fields for rule configuration (scene, condition, target, priority, enabled)
  • Efficient index on [scene, enabled, priority(sort: Desc), ruleId] supporting the query pattern in AutoModelRoutingService
  • Flexible JSON columns for condition and target definitions
  • Standard timestamp fields for audit tracking

2216-2248: LGTM! Comprehensive routing result tracking schema.

The AutoModelRoutingResult model effectively tracks routing decisions with:

  • Association to action results via actionResultId and actionResultVersion
  • Complete routing metadata (scene, strategy, matched rule, original/selected model IDs)
  • Unique constraint on [actionResultId, actionResultVersion] preventing duplicate entries
  • Index on [userId, createdAt(sort: Desc)] for efficient user-based queries
apps/api/src/modules/provider/provider.module.ts (1)

4-11: LGTM! Proper NestJS module integration.

The AutoModelRoutingService is correctly:

  • Imported from the local module
  • Added to the providers array for dependency injection
  • Added to the exports array for use by other modules (e.g., SkillService)

This follows NestJS best practices for module structure and dependency injection.

apps/api/src/modules/skill/skill.service.ts (4)

47-47: LGTM! Proper imports and dependency injection.

The changes correctly:

  • Import getModelSceneFromMode utility from @refly/utils
  • Import AutoModelRoutingService and RoutingContext from the provider module
  • Inject AutoModelRoutingService via constructor following NestJS patterns

Also applies to: 69-69, 109-109


487-537: LGTM! Well-structured routing context and primary scene routing.

The implementation correctly:

  • Builds RoutingContext with all necessary fields (lines 494-502): llmItems, userId, actionResultId, actionResultVersion, mode, inputPrompt, and toolsets
  • Derives the primary scene using getModelSceneFromMode(param.mode) (line 513)
  • Routes only the primary model through AutoModelRoutingService (lines 530-533)
  • Preserves auxiliary models (titleGeneration, queryAnalysis, media) unchanged (lines 534-537)
  • Properly handles the originalProviderItem assignment (lines 518-526)

This aligns with the PR objectives of routing only the primary scene while maintaining other models.


549-565: LGTM! Proper routing metadata injection.

The code correctly:

  • Injects routedData into the provider item config when routing occurs (lines 549-561)
  • Marks the config with isRouted: true and original model information
  • Calculates isAutoModelRouted by comparing actual vs surface provider item IDs (lines 563-565)

This provides good observability for routing decisions.


837-841: LGTM! Consistent scene derivation for model name selection.

The getModelNameForMode helper correctly uses getModelSceneFromMode to derive the scene from the mode, then retrieves the model ID from the corresponding scene in modelConfigMap. This is consistent with the routing logic changes.

apps/api/src/modules/provider/auto-model-router.service.ts (6)

19-54: LGTM! Well-defined routing types.

The routing type definitions are clear and extensible:

  • RuleCondition uses explicit AND logic for multiple conditions with good documentation
  • RoutingStrategy enum covers all 5 routing tiers (rule-based, tool-based, and 3 fallbacks)
  • RoutingTarget and RuleRouteResult provide clean interfaces for routing decisions

The JSDoc comments explain the purpose and usage of each type.


60-97: LGTM! Comprehensive routing context interface.

The RoutingContext interface is well-designed with:

  • All necessary fields for routing decisions (llmItems, userId, actionResult info, mode, input, toolsets)
  • Clear JSDoc documentation for each field
  • Important privacy note about inputPrompt (line 88-89) indicating it's used for matching but not stored

The expanded context supports the new rule-based routing requirements.


103-190: LGTM! Clean rule matching implementation.

The RuleRouter class is well-structured:

  • Encapsulates all rule matching logic without external dependencies
  • route() method iterates through rules by priority and returns first match
  • matchCondition() properly implements AND logic for conditions
  • matchToolsetInventoryKeys() correctly checks if any toolset inventory key matches (lines 163-179)
  • Clean separation of concerns with private helper methods

The implementation is straightforward and correct.


204-377: LGTM! Robust caching implementation with proper lifecycle management.

The RuleCache class is well-implemented:

  • Uses TTL-based in-memory caching (3-minute TTL on line 217)
  • Implements periodic refresh to keep rules updated (lines 335-342)
  • Deep comparison in hasRulesChanged() prevents unnecessary log spam (lines 285-329)
  • Proper error handling returns cached rules if database fetch fails (lines 273-278)
  • Timer cleanup via OnModuleDestroy prevents resource leaks (lines 374-376)
  • timer.unref() (line 341) ensures the timer doesn't prevent process exit

The 3-minute cache TTL provides a good balance between database load and rule freshness for most use cases.


412-500: LGTM! Well-structured multi-tier routing implementation.

The route() method correctly implements the 5-tier routing strategy:

  1. Rule-based routing from database (lines 425-439)
  2. Tool-based routing from environment (lines 442-453)
  3. Random selection (lines 455-467)
  4. Built-in priority list (lines 469-481)
  5. First available fallback (lines 483-497)

Key strengths:

  • Early return for non-Auto models (lines 417-419)
  • Each tier properly falls through to the next if no match
  • Non-blocking routing result persistence via saveRoutingResult()
  • Clear error when no models available (line 499)

The implementation aligns well with the PR objectives.


506-675: LGTM! Helper methods and persistence logic are well-implemented.

The helper methods are clean and correct:

Model filtering and selection:

  • buildModelMap() (lines 506-520) properly filters invalid configs and excludes reasoning models (line 513, per learnings)
  • routeByRules() (lines 526-534) efficiently uses RuleCache and RuleRouter
  • routeByTools() (lines 545-582) correctly checks mode and scene before applying tool-based routing
  • routeByRandomSelection() and routeByBuiltInPriorityList() (lines 587-617) implement fallback strategies

Routing result persistence:

  • saveRoutingResult() (lines 619-637) wraps async save with error handling
  • Non-blocking persistence prevents routing from being blocked by database operations
  • saveRoutingResultAsync() (lines 642-667) saves comprehensive routing metadata including user, action result, scene, strategy, matched rule, and model IDs
  • getModelIdFromProviderItem() (lines 669-675) safely extracts model ID with null checks

All implementations follow best practices and handle edge cases properly.

Based on learnings, the exclusion of reasoning models from auto-routing in buildModelMap (line 513) is an intentional design decision.


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

@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

🧹 Nitpick comments (2)
apps/api/src/modules/provider/auto-model-router.service.ts (2)

405-421: Potential fragility in scene derivation logic.

The deriveSceneFromMode method uses string.includes() which could be fragile if future modes contain multiple keywords. For example, a hypothetical mode like 'copilot_agent_hybrid' would match 'copilot' first (which is correct due to the order), but this behavior relies on the order of checks.

Consider using exact string matching or a lookup map for more robust mode-to-scene mapping, especially since getModelSceneFromMode in packages/utils/src/models.ts uses exact equality checks.

🔎 Alternative: Use the centralized helper
- private deriveSceneFromMode(mode?: string): string {
-   if (!mode) {
-     return 'chat';
-   }
-
-   // Map mode to scene
-   if (mode.includes('copilot')) {
-     return 'copilot';
-   }
-
-   if (mode.includes('agent')) {
-     return 'agent';
-   }
-
-   // Default fallback
-   return 'chat';
- }
+ private deriveSceneFromMode(mode?: string): string {
+   // Use the centralized helper from utils
+   return getModelSceneFromMode(mode);
+ }

This ensures consistency with the mode-to-scene mapping used throughout the codebase.


437-470: Improve documentation and add logging for scene mismatch during routing.

The conditional persistence at lines 448-449 is documented in the JSDoc (@param externalScene), but the logic needs better inline explanation and observability:

  1. Add an inline comment explaining the business reason for conditional persistence (why scenes should match)
  2. Add debug logging when scenes don't match to aid troubleshooting and routing analysis
  3. Verify this conditional persistence pattern is applied consistently across all routing strategies (confirmed: it is applied to rule-based, tool-based, and fallback paths)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ce316a and 8b18d05.

📒 Files selected for processing (7)
  • apps/api/prisma/schema.prisma
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/skill/skill.service.ts
  • packages/skill-template/src/skills/agent.ts
  • packages/utils/src/id.ts
  • packages/utils/src/models.ts
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{js,ts,jsx,tsx}: Always use optional chaining (?.) when accessing object properties
Always use nullish coalescing (??) or default values for potentially undefined values
Always check array existence before using array methods
Always validate object properties before destructuring
Always use single quotes for string literals in JavaScript/TypeScript code

**/*.{js,ts,jsx,tsx}: Use semicolons at the end of statements
Include spaces around operators (e.g., a + b instead of a+b)
Always use curly braces for control statements
Place opening braces on the same line as their statement

**/*.{js,ts,jsx,tsx}: Group import statements in order: React/framework libraries, third-party libraries, internal modules, relative path imports, type imports, style imports
Sort imports alphabetically within each import group
Leave a blank line between import groups
Extract complex logic into custom hooks
Use functional updates for state (e.g., setCount(prev => prev + 1))
Split complex state into multiple state variables rather than single large objects
Use useReducer for complex state logic instead of multiple useState calls

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{js,ts,tsx,jsx,py,java,cpp,c,cs,rb,go,rs,php,swift,kt,scala,r,m,mm,sql}

📄 CodeRabbit inference engine (.cursor/rules/00-language-priority.mdc)

**/*.{js,ts,tsx,jsx,py,java,cpp,c,cs,rb,go,rs,php,swift,kt,scala,r,m,mm,sql}: All code comments MUST be written in English
All variable names, function names, class names, and other identifiers MUST use English words
Comments should be concise and explain 'why' rather than 'what'
Use proper grammar and punctuation in comments
Keep comments up-to-date when code changes
Document complex logic, edge cases, and important implementation details
Use clear, descriptive names that indicate purpose
Avoid abbreviations unless they are universally understood

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/00-language-priority.mdc)

Use JSDoc style comments for functions and classes in JavaScript/TypeScript

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/01-code-style.mdc)

**/*.{js,jsx,ts,tsx}: Use single quotes for string literals in TypeScript/JavaScript
Always use optional chaining (?.) when accessing object properties in TypeScript/JavaScript
Always use nullish coalescing (??) or default values for potentially undefined values in TypeScript/JavaScript
Always check array existence before using array methods in TypeScript/JavaScript
Validate object properties before destructuring in TypeScript/JavaScript
Use ES6+ features like arrow functions, destructuring, and spread operators in TypeScript/JavaScript
Avoid magic numbers and strings - use named constants in TypeScript/JavaScript
Use async/await instead of raw promises for asynchronous code in TypeScript/JavaScript

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/03-typescript-guidelines.mdc)

**/*.{ts,tsx}: Avoid using any type whenever possible - use unknown type instead with proper type guards
Always define explicit return types for functions, especially for public APIs
Prefer extending existing types over creating entirely new types
Use TypeScript utility types (Partial<T>, Pick<T, K>, Omit<T, K>, Readonly<T>, Record<K, T>) to derive new types
Use union types and intersection types to combine existing types
Always import types explicitly using the import type syntax
Group type imports separately from value imports
Minimize creating local type aliases for imported types

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{js,ts,jsx,tsx,css,json}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

Maximum line length of 100 characters

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{js,ts,jsx,tsx,css,json,yml,yaml}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

Use 2 spaces for indentation, no tabs

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{js,ts,jsx,tsx,css,json,yml,yaml,md}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

No trailing whitespace at the end of lines

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{css,scss,sass,less,js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/09-design-system.mdc)

**/*.{css,scss,sass,less,js,jsx,ts,tsx}: Primary color (#155EEF) should be used for main brand color in buttons, links, and accents
Error color (#F04438) should be used for error states and destructive actions
Success color (#12B76A) should be used for success states and confirmations
Warning color (#F79009) should be used for warnings and important notifications
Info color (#0BA5EC) should be used for informational elements

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)

**/*.{tsx,ts}: Use the translation wrapper component and useTranslation hook in components
Ensure all user-facing text is translatable

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{tsx,ts,json}

📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)

Support dynamic content with placeholders in translations

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{tsx,ts,jsx,js,vue,css,scss,less}

📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)

**/*.{tsx,ts,jsx,js,vue,css,scss,less}: Use the primary blue (#155EEF) for main UI elements, CTAs, and active states
Use red (#F04438) only for errors, warnings, and destructive actions
Use green (#12B76A) for success states and confirmations
Use orange (#F79009) for warning states and important notifications
Use blue (#0BA5EC) for informational elements
Primary buttons should be solid with the primary color
Secondary buttons should have a border with transparent or light background
Danger buttons should use the error color
Use consistent padding, border radius, and hover states for all buttons
Follow fixed button sizes based on their importance and context
Use consistent border radius (rounded-lg) for all cards
Apply light shadows (shadow-sm) for card elevation
Maintain consistent padding inside cards (p-4 or p-6)
Use subtle borders for card separation
Ensure proper spacing between card elements
Apply consistent styling to all form inputs
Use clear visual indicators for focus, hover, and error states in form elements
Apply proper spacing between elements using 8px, 16px, 24px increments
Ensure proper alignment of elements (left, center, or right)
Use responsive layouts that work across different device sizes
Maintain a minimum contrast ratio of 4.5:1 for text

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{tsx,ts,jsx,js,vue}

📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)

**/*.{tsx,ts,jsx,js,vue}: Include appropriate loading states for async actions in buttons
Group related form elements with appropriate spacing
Provide clear validation feedback for forms
Ensure proper labeling and accessibility for form elements
Ensure all interactive elements are keyboard accessible
Include appropriate ARIA attributes for complex components
Provide alternative text for images and icons
Support screen readers with semantic HTML elements

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/08-contributing-guidelines.mdc)

**/*.{ts,tsx,js,jsx}: Follow the TypeScript/JavaScript style guidelines
Ensure code is well-tested and documented

Files:

  • packages/skill-template/src/skills/agent.ts
  • apps/api/src/modules/provider/provider.module.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/models.ts
  • apps/api/src/modules/skill/skill.service.ts
apps/api/src/**/*.{controller,service}.ts

📄 CodeRabbit inference engine (.cursor/rules/06-api-structure.mdc)

Implement proper error handling in API modules

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/skill/skill.service.ts
🧠 Learnings (2)
📚 Learning: 2025-11-25T03:04:12.836Z
Learnt from: CR
Repo: refly-ai/refly PR: 0
File: .cursor/rules/06-api-structure.mdc:0-0
Timestamp: 2025-11-25T03:04:12.836Z
Learning: Applies to apps/api/src/{auth,user,project,canvas,rag,knowledge,search,skill,share,code-artifact}/**/*.{controller,service}.ts : Use dependency injection for module dependencies in NestJS

Applied to files:

  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/skill/skill.service.ts
📚 Learning: 2025-11-25T03:04:12.836Z
Learnt from: CR
Repo: refly-ai/refly PR: 0
File: .cursor/rules/06-api-structure.mdc:0-0
Timestamp: 2025-11-25T03:04:12.836Z
Learning: Applies to apps/api/src/{auth,user,project,canvas,rag,knowledge,search,skill,share,code-artifact}/**/*.{ts,tsx} : Follow NestJS module structure with controllers, services, and DTOs in dedicated module directories

Applied to files:

  • apps/api/src/modules/provider/provider.module.ts
🧬 Code graph analysis (3)
packages/skill-template/src/skills/agent.ts (4)
packages/utils/src/models.ts (1)
  • getModelSceneFromMode (10-14)
packages/openapi-schema/src/types.gen.ts (1)
  • mode (34-34)
packages/request/src/requests/types.gen.ts (1)
  • mode (34-34)
packages/ai-workspace-common/src/requests/types.gen.ts (1)
  • mode (34-34)
apps/api/src/modules/provider/auto-model-router.service.ts (5)
packages/openapi-schema/src/types.gen.ts (2)
  • mode (34-34)
  • LLMModelConfig (5937-5966)
apps/api/src/modules/tool/utils/token.ts (1)
  • safeParseJSON (371-377)
apps/api/src/modules/tool/sandbox/scalebox.dto.ts (1)
  • error (218-224)
packages/utils/src/auto-model.ts (3)
  • isAutoModel (92-112)
  • selectAutoModel (41-48)
  • AUTO_MODEL_ROUTING_PRIORITY (14-20)
packages/utils/src/id.ts (1)
  • genRoutingResultID (329-331)
apps/api/src/modules/skill/skill.service.ts (2)
apps/api/src/modules/provider/auto-model-router.service.ts (1)
  • RoutingContext (59-96)
packages/utils/src/models.ts (1)
  • getModelSceneFromMode (10-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build / Build
🔇 Additional comments (16)
packages/utils/src/models.ts (1)

5-14: LGTM!

The getModelSceneFromMode function provides clear, centralized mode-to-scene mapping logic. The implementation correctly handles the three mode cases with appropriate fallback to 'chat'.

packages/skill-template/src/skills/agent.ts (3)

20-20: LGTM!

Good refactoring to use the centralized getModelSceneFromMode helper, which improves maintainability and consistency across the codebase.


109-110: LGTM!

The replacement of inline conditional logic with getModelSceneFromMode(mode) correctly implements the centralized mapping, and the updated comment accurately reflects the new node_agent → agent scene mapping.


137-138: LGTM!

Consistent usage of getModelSceneFromMode(mode) ensures uniform scene resolution across both the preprocessing and initialization phases.

apps/api/prisma/schema.prisma (2)

2186-2222: LGTM!

The AutoModelRoutingRule model is well-designed with appropriate fields, indexes, and constraints:

  • Unique constraint on [scene, ruleName] prevents duplicate rules per scene
  • Composite index on [scene, enabled, priority(sort: Desc), ruleId] efficiently supports the priority-based rule lookup pattern used in the service
  • JSON storage for condition and target provides necessary flexibility for rule matching

2224-2264: LGTM!

The AutoModelRoutingResult model appropriately tracks routing decisions:

  • Unique constraint on [actionResultId, actionResultVersion] ensures one routing result per action execution
  • Index on [userId, createdAt(sort: Desc)] supports efficient user-scoped queries
  • Optional actionResultId and actionResultVersion correctly handle cases where routing occurs before action creation
  • Denormalized matchedRuleName field improves query performance for viewing routing decisions
packages/utils/src/id.ts (2)

54-55: LGTM!

The new ID prefixes follow the established naming convention and provide clear identifiers for routing-related entities.


329-335: LGTM!

The ID generator functions are consistent with existing patterns in the file and correctly use the new prefixes.

apps/api/src/modules/provider/provider.module.ts (1)

4-11: LGTM!

The module correctly registers and exports AutoModelRoutingService, following NestJS best practices for dependency injection and module organization.

Based on learnings, this follows the established pattern for module structure in the API.

apps/api/src/modules/skill/skill.service.ts (4)

510-524: LGTM!

The routing implementation correctly targets only the primary scene while preserving auxiliary scenes (titleGeneration, queryAnalysis, image, video, audio). This design prevents unintended routing side effects on non-primary models.


833-836: LGTM!

The refactoring to use getModelSceneFromMode for modelName derivation ensures consistency with the routing logic and correctly uses the routed modelConfigMap.


529-557: Clarify surface model tracking vs. execution/billing model separation.

The distinction between tracked model (param.modelItemId - for UI/audit) and execution model (providerItem - for actual execution and billing) requires clarification:

  1. Line 532-534: param.modelItemId is set to original itemId to track the user's selected model for UI and audit purposes, not billing.
  2. Line 536-539: providerItem (resolved via routed itemId) is used for execution, and its creditBilling (line 570) determines the actual cost charged to users.
  3. Line 546-557: routedData injection correctly preserves original model IDs for audit tracking when auto-routing occurs.

The current design charges users for the model they actually receive (routed), while preserving the original selection in metadata. Verify this billing approach aligns with intended business logic—users should understand they pay for executed models, not selected models.

Likely an incorrect or invalid review comment.


487-502: inputPrompt field is not currently used in routing logic.

The routing service does not consume inputPrompt despite its JSDoc indicating it's "for regex matching." The RoutingContext.inputPrompt field is passed but never referenced in RuleRouter or any routing decision logic. The RuleCondition interface only evaluates toolsetInventoryKeys. Either remove the unused field or implement the regex matching logic if planned for future use.

The actionResultVersion versioning logic is correct and consistent with the Prisma schema (version Int @default(0)) and action result creation logic elsewhere in the file.

apps/api/src/modules/provider/auto-model-router.service.ts (3)

203-376: LGTM!

The RuleCache implementation demonstrates solid design:

  • 3-minute TTL with periodic refresh provides a good balance between freshness and performance
  • unref() on the timer (line 340) correctly prevents blocking process exit
  • Deep comparison in hasRulesChanged ensures accurate change detection
  • Fallback to cached rules on fetch failure (lines 272-276) provides resilience

658-706: LGTM!

The non-blocking saveRoutingResult pattern (lines 667-676) correctly prevents routing persistence failures from affecting the main request flow. The async save operation includes proper error handling with logging.


162-178: The nested optional chaining pattern toolset.toolset?.key is intentional and consistent across the codebase. This structure is confirmed in the external @refly/openapi-schema package's GenericToolset type, which contains a nested toolset property. The same pattern appears in 8+ other files (tool.service.ts, composio.service.ts, pilot-engine.ts, workflow-app.service.ts, intent-analysis.service.ts, etc.), confirming this is the established design. The optional chaining is properly applied per coding guidelines, and the truthiness check on line 172 (if (inventoryKey && keysSet.has(inventoryKey))) safely handles the potential undefined value. No changes are needed.

@nettee nettee force-pushed the feat/920/auto-model-routing-rule branch from 8b18d05 to c4279d0 Compare December 22, 2025 07:04
Copy link

@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: 0

♻️ Duplicate comments (1)
apps/api/src/modules/provider/auto-model-router.service.ts (1)

545-559: Reasoning model exclusion is intentional.

Lines 551-552 explicitly exclude models with capabilities.reasoning === true from auto-routing. This exclusion is documented in comments and appears intentional, likely to ensure reasoning models are only used when explicitly selected.

However, this design choice lacks configuration flexibility. If there are scenarios where reasoning models should be available for auto-routing, consider making this behavior configurable.

This concern was already raised in past review comments on this file.

🧹 Nitpick comments (3)
apps/api/src/modules/skill/skill.service.ts (1)

510-539: Consider null safety for routedProviderItem.

At line 518-520, await this.autoModelRoutingService.route() returns a ProviderItemModel, but there's a possibility that modelProviderMap[primaryScene] could be undefined if routing fails for some reason. Consider adding explicit null checks or using optional chaining when accessing routedProviderItem at line 530.

🔎 Suggested defensive coding
-    const originalProviderItem = originalModelProviderMap[primaryScene];
-    const routedProviderItem = modelProviderMap[primaryScene];
+    const originalProviderItem = originalModelProviderMap[primaryScene];
+    const routedProviderItem = modelProviderMap[primaryScene];
+
+    if (!originalProviderItem) {
+      throw new ProviderItemNotFoundError(`No provider item found for scene ${primaryScene}`);
+    }
apps/api/prisma/schema.prisma (1)

2187-2214: LGTM! Well-designed schema for routing rules.

The AutoModelRoutingRule model has:

  • Proper indexing for efficient query by scene, enabled status, and priority
  • UUID-based ruleId for distributed systems
  • Consistent field naming and type choices

Consider implementing application-level validation for the JSON fields (condition and target) to ensure data integrity beyond schema constraints.

apps/api/src/modules/provider/auto-model-router.service.ts (1)

203-376: LGTM! Robust cache implementation with lifecycle management.

The RuleCache class demonstrates several best practices:

  • Proper lifecycle cleanup via OnModuleDestroy (line 373)
  • Non-blocking timer with unref() at line 340
  • Fallback to expired cache on database errors (lines 274-276)
  • Deep comparison for change detection

The 3-minute TTL with periodic refresh balances freshness with database load.

Consider adding a maximum cache size limit to prevent unbounded growth if many scenes are used over time.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8b18d05 and c4279d0.

📒 Files selected for processing (7)
  • apps/api/prisma/schema.prisma
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/skill/skill.service.ts
  • packages/skill-template/src/skills/agent.ts
  • packages/utils/src/id.ts
  • packages/utils/src/models.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/utils/src/id.ts
  • packages/utils/src/models.ts
  • packages/skill-template/src/skills/agent.ts
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{js,ts,jsx,tsx}: Always use optional chaining (?.) when accessing object properties
Always use nullish coalescing (??) or default values for potentially undefined values
Always check array existence before using array methods
Always validate object properties before destructuring
Always use single quotes for string literals in JavaScript/TypeScript code

**/*.{js,ts,jsx,tsx}: Use semicolons at the end of statements
Include spaces around operators (e.g., a + b instead of a+b)
Always use curly braces for control statements
Place opening braces on the same line as their statement

**/*.{js,ts,jsx,tsx}: Group import statements in order: React/framework libraries, third-party libraries, internal modules, relative path imports, type imports, style imports
Sort imports alphabetically within each import group
Leave a blank line between import groups
Extract complex logic into custom hooks
Use functional updates for state (e.g., setCount(prev => prev + 1))
Split complex state into multiple state variables rather than single large objects
Use useReducer for complex state logic instead of multiple useState calls

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{js,ts,tsx,jsx,py,java,cpp,c,cs,rb,go,rs,php,swift,kt,scala,r,m,mm,sql}

📄 CodeRabbit inference engine (.cursor/rules/00-language-priority.mdc)

**/*.{js,ts,tsx,jsx,py,java,cpp,c,cs,rb,go,rs,php,swift,kt,scala,r,m,mm,sql}: All code comments MUST be written in English
All variable names, function names, class names, and other identifiers MUST use English words
Comments should be concise and explain 'why' rather than 'what'
Use proper grammar and punctuation in comments
Keep comments up-to-date when code changes
Document complex logic, edge cases, and important implementation details
Use clear, descriptive names that indicate purpose
Avoid abbreviations unless they are universally understood

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/00-language-priority.mdc)

Use JSDoc style comments for functions and classes in JavaScript/TypeScript

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/01-code-style.mdc)

**/*.{js,jsx,ts,tsx}: Use single quotes for string literals in TypeScript/JavaScript
Always use optional chaining (?.) when accessing object properties in TypeScript/JavaScript
Always use nullish coalescing (??) or default values for potentially undefined values in TypeScript/JavaScript
Always check array existence before using array methods in TypeScript/JavaScript
Validate object properties before destructuring in TypeScript/JavaScript
Use ES6+ features like arrow functions, destructuring, and spread operators in TypeScript/JavaScript
Avoid magic numbers and strings - use named constants in TypeScript/JavaScript
Use async/await instead of raw promises for asynchronous code in TypeScript/JavaScript

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/03-typescript-guidelines.mdc)

**/*.{ts,tsx}: Avoid using any type whenever possible - use unknown type instead with proper type guards
Always define explicit return types for functions, especially for public APIs
Prefer extending existing types over creating entirely new types
Use TypeScript utility types (Partial<T>, Pick<T, K>, Omit<T, K>, Readonly<T>, Record<K, T>) to derive new types
Use union types and intersection types to combine existing types
Always import types explicitly using the import type syntax
Group type imports separately from value imports
Minimize creating local type aliases for imported types

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{js,ts,jsx,tsx,css,json}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

Maximum line length of 100 characters

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{js,ts,jsx,tsx,css,json,yml,yaml}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

Use 2 spaces for indentation, no tabs

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{js,ts,jsx,tsx,css,json,yml,yaml,md}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

No trailing whitespace at the end of lines

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
apps/api/src/**/*.{controller,service}.ts

📄 CodeRabbit inference engine (.cursor/rules/06-api-structure.mdc)

Implement proper error handling in API modules

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{css,scss,sass,less,js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/09-design-system.mdc)

**/*.{css,scss,sass,less,js,jsx,ts,tsx}: Primary color (#155EEF) should be used for main brand color in buttons, links, and accents
Error color (#F04438) should be used for error states and destructive actions
Success color (#12B76A) should be used for success states and confirmations
Warning color (#F79009) should be used for warnings and important notifications
Info color (#0BA5EC) should be used for informational elements

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)

**/*.{tsx,ts}: Use the translation wrapper component and useTranslation hook in components
Ensure all user-facing text is translatable

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{tsx,ts,json}

📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)

Support dynamic content with placeholders in translations

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{tsx,ts,jsx,js,vue,css,scss,less}

📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)

**/*.{tsx,ts,jsx,js,vue,css,scss,less}: Use the primary blue (#155EEF) for main UI elements, CTAs, and active states
Use red (#F04438) only for errors, warnings, and destructive actions
Use green (#12B76A) for success states and confirmations
Use orange (#F79009) for warning states and important notifications
Use blue (#0BA5EC) for informational elements
Primary buttons should be solid with the primary color
Secondary buttons should have a border with transparent or light background
Danger buttons should use the error color
Use consistent padding, border radius, and hover states for all buttons
Follow fixed button sizes based on their importance and context
Use consistent border radius (rounded-lg) for all cards
Apply light shadows (shadow-sm) for card elevation
Maintain consistent padding inside cards (p-4 or p-6)
Use subtle borders for card separation
Ensure proper spacing between card elements
Apply consistent styling to all form inputs
Use clear visual indicators for focus, hover, and error states in form elements
Apply proper spacing between elements using 8px, 16px, 24px increments
Ensure proper alignment of elements (left, center, or right)
Use responsive layouts that work across different device sizes
Maintain a minimum contrast ratio of 4.5:1 for text

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{tsx,ts,jsx,js,vue}

📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)

**/*.{tsx,ts,jsx,js,vue}: Include appropriate loading states for async actions in buttons
Group related form elements with appropriate spacing
Provide clear validation feedback for forms
Ensure proper labeling and accessibility for form elements
Ensure all interactive elements are keyboard accessible
Include appropriate ARIA attributes for complex components
Provide alternative text for images and icons
Support screen readers with semantic HTML elements

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/08-contributing-guidelines.mdc)

**/*.{ts,tsx,js,jsx}: Follow the TypeScript/JavaScript style guidelines
Ensure code is well-tested and documented

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
🧠 Learnings (2)
📚 Learning: 2025-11-25T03:04:12.836Z
Learnt from: CR
Repo: refly-ai/refly PR: 0
File: .cursor/rules/06-api-structure.mdc:0-0
Timestamp: 2025-11-25T03:04:12.836Z
Learning: Applies to apps/api/src/{auth,user,project,canvas,rag,knowledge,search,skill,share,code-artifact}/**/*.{controller,service}.ts : Use dependency injection for module dependencies in NestJS

Applied to files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
📚 Learning: 2025-11-25T03:04:12.836Z
Learnt from: CR
Repo: refly-ai/refly PR: 0
File: .cursor/rules/06-api-structure.mdc:0-0
Timestamp: 2025-11-25T03:04:12.836Z
Learning: Applies to apps/api/src/{auth,user,project,canvas,rag,knowledge,search,skill,share,code-artifact}/**/*.{ts,tsx} : Follow NestJS module structure with controllers, services, and DTOs in dedicated module directories

Applied to files:

  • apps/api/src/modules/provider/provider.module.ts
🧬 Code graph analysis (1)
apps/api/src/modules/skill/skill.service.ts (2)
apps/api/src/modules/provider/auto-model-router.service.ts (1)
  • RoutingContext (59-96)
packages/utils/src/models.ts (1)
  • getModelSceneFromMode (10-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build / Build
🔇 Additional comments (10)
apps/api/src/modules/provider/provider.module.ts (1)

4-4: LGTM! Proper NestJS module integration.

The AutoModelRoutingService is correctly registered as a provider and exported for dependency injection, following NestJS best practices.

Based on learnings, this aligns with the requirement to use dependency injection for module dependencies in NestJS.

Also applies to: 10-11

apps/api/src/modules/skill/skill.service.ts (3)

47-47: LGTM! Clean integration of routing dependencies.

The imports and constructor injection properly integrate the AutoModelRoutingService and supporting utilities. The dependency injection follows NestJS best practices.

Also applies to: 69-69, 109-109


833-836: LGTM! Cleaner model name resolution.

The refactored getModelNameForMode function using getModelSceneFromMode provides consistent scene mapping and is more maintainable than the previous mode-based conditionals.


487-502: No privacy concern - inputPrompt is correctly excluded from routing result persistence.

The inputPrompt field is passed to the routing context but is not persisted to the database, not logged, and not used in any routing logic. The saveRoutingResultAsync method saves only the routing decision metadata (strategy, matched rule, selected model), explicitly excluding user input. The existing code already implements the privacy protection stated in the comment.

Likely an incorrect or invalid review comment.

apps/api/src/modules/provider/auto-model-router.service.ts (5)

18-96: LGTM! Well-structured type definitions.

The interfaces and enums provide clear contracts for the routing system:

  • RuleCondition supports extensible matching logic
  • RoutingStrategy enum covers all fallback tiers
  • RoutingContext includes comprehensive context for routing decisions

The JSDoc comments are helpful and follow the coding guidelines for explaining 'why' rather than 'what'.


102-189: LGTM! Clean rule matching implementation.

The RuleRouter class provides well-encapsulated rule matching logic:

  • Stateless design with context injection
  • Efficient Set-based toolset matching at lines 168-176
  • Proper null handling for invalid rules
  • Clear separation of concerns

The implementation follows SOLID principles effectively.


437-539: LGTM! Well-structured multi-tier routing strategy.

The route() method implements a clear priority cascade:

  1. Rule-based routing
  2. Tool-based routing
  3. Random selection
  4. Built-in priority list
  5. First available fallback

The scene validation at lines 448-449 ensures routing results are only saved when they match the expected context, preventing data inconsistencies.


584-621: LGTM! Clear tool-based routing logic.

The routeByTools method has explicit guards:

  • Only applies to node_agent mode with agent scene (lines 590-596)
  • Checks environment configuration before proceeding
  • Handles missing models gracefully

This temporary strategy is well-isolated and can be easily removed or modified later.


658-714: LGTM! Non-blocking result persistence with proper error handling.

The routing result persistence is designed correctly:

  • Async operation that doesn't block routing decisions (line 667)
  • Errors are logged but don't fail the request (line 675)
  • Comprehensive metadata captured for observability

This fire-and-forget approach is appropriate for non-critical tracking data.

apps/api/prisma/schema.prisma (1)

2217-2248: Verify nullable fields in the unique constraint on lines 2224–2225 and 2245.

Both actionResultId and actionResultVersion are nullable but included in the composite unique constraint @@unique([actionResultId, actionResultVersion]). Prisma requires all fields that make up a unique constraint to be mandatory fields. Either make these fields required (non-nullable) if all routing results must have an associated action, or remove them from the unique constraint and use a different approach to prevent duplicates.

@nettee nettee force-pushed the feat/920/auto-model-routing-rule branch from c4279d0 to b499efb Compare December 22, 2025 08:41
Copy link

@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

🧹 Nitpick comments (2)
apps/api/prisma/schema.prisma (1)

2190-2190: Consider using consistent ID generation pattern.

The ruleId field uses @default(uuid()) which generates PostgreSQL UUIDs, while other entities in the schema use CUID2-based IDs (e.g., genSkillID(), genActionResultID()). The codebase includes genRoutingRuleID() in the utilities package, but it's not referenced in the Prisma schema default.

This inconsistency could lead to:

  • Mixed ID formats across the database
  • Difficulty in identifying entity types from ID prefixes
  • Inconsistent ID generation patterns
Consider aligning with the existing ID generation pattern

If CUID2-based IDs are preferred (as suggested by the presence of genRoutingRuleID()), consider either:

  1. Removing the @default(uuid()) and generating IDs in application code using genRoutingRuleID(), or
  2. If PostgreSQL UUIDs are intentionally chosen for this model, document the rationale
apps/api/src/modules/provider/auto-model-router.service.ts (1)

105-130: Consider logging when rule target model is not found.

When a rule matches but the target model is not in the modelMap (line 116-126), the router silently continues to the next rule. This could mask configuration errors where rules reference models that don't exist or aren't available to the user.

Suggested logging enhancement

Add logging when a matched rule's target model is unavailable:

 const selectedModel = this.selectModelFromTarget(target, modelMap);
 if (selectedModel) {
   return {
     providerItem: selectedModel,
     matchedRule: {
       ruleId: rule.ruleId,
       ruleName: rule.ruleName,
     },
   };
+} else {
+  // Log when rule matches but target model is not available
+  this.logger?.debug(
+    `Rule '${rule.ruleName}' (${rule.ruleId}) matched but target model '${target.model}' not found`
+  );
 }

Note: Would need to inject logger into RuleRouter or pass it in constructor.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c4279d0 and b499efb.

📒 Files selected for processing (7)
  • apps/api/prisma/schema.prisma
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/skill/skill.service.ts
  • packages/skill-template/src/skills/agent.ts
  • packages/utils/src/id.ts
  • packages/utils/src/models.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/skill-template/src/skills/agent.ts
  • packages/utils/src/models.ts
  • packages/utils/src/id.ts
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{js,ts,jsx,tsx}: Always use optional chaining (?.) when accessing object properties
Always use nullish coalescing (??) or default values for potentially undefined values
Always check array existence before using array methods
Always validate object properties before destructuring
Always use single quotes for string literals in JavaScript/TypeScript code

**/*.{js,ts,jsx,tsx}: Use semicolons at the end of statements
Include spaces around operators (e.g., a + b instead of a+b)
Always use curly braces for control statements
Place opening braces on the same line as their statement

**/*.{js,ts,jsx,tsx}: Group import statements in order: React/framework libraries, third-party libraries, internal modules, relative path imports, type imports, style imports
Sort imports alphabetically within each import group
Leave a blank line between import groups
Extract complex logic into custom hooks
Use functional updates for state (e.g., setCount(prev => prev + 1))
Split complex state into multiple state variables rather than single large objects
Use useReducer for complex state logic instead of multiple useState calls

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{js,ts,tsx,jsx,py,java,cpp,c,cs,rb,go,rs,php,swift,kt,scala,r,m,mm,sql}

📄 CodeRabbit inference engine (.cursor/rules/00-language-priority.mdc)

**/*.{js,ts,tsx,jsx,py,java,cpp,c,cs,rb,go,rs,php,swift,kt,scala,r,m,mm,sql}: All code comments MUST be written in English
All variable names, function names, class names, and other identifiers MUST use English words
Comments should be concise and explain 'why' rather than 'what'
Use proper grammar and punctuation in comments
Keep comments up-to-date when code changes
Document complex logic, edge cases, and important implementation details
Use clear, descriptive names that indicate purpose
Avoid abbreviations unless they are universally understood

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/00-language-priority.mdc)

Use JSDoc style comments for functions and classes in JavaScript/TypeScript

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/01-code-style.mdc)

**/*.{js,jsx,ts,tsx}: Use single quotes for string literals in TypeScript/JavaScript
Always use optional chaining (?.) when accessing object properties in TypeScript/JavaScript
Always use nullish coalescing (??) or default values for potentially undefined values in TypeScript/JavaScript
Always check array existence before using array methods in TypeScript/JavaScript
Validate object properties before destructuring in TypeScript/JavaScript
Use ES6+ features like arrow functions, destructuring, and spread operators in TypeScript/JavaScript
Avoid magic numbers and strings - use named constants in TypeScript/JavaScript
Use async/await instead of raw promises for asynchronous code in TypeScript/JavaScript

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/03-typescript-guidelines.mdc)

**/*.{ts,tsx}: Avoid using any type whenever possible - use unknown type instead with proper type guards
Always define explicit return types for functions, especially for public APIs
Prefer extending existing types over creating entirely new types
Use TypeScript utility types (Partial<T>, Pick<T, K>, Omit<T, K>, Readonly<T>, Record<K, T>) to derive new types
Use union types and intersection types to combine existing types
Always import types explicitly using the import type syntax
Group type imports separately from value imports
Minimize creating local type aliases for imported types

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{js,ts,jsx,tsx,css,json}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

Maximum line length of 100 characters

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{js,ts,jsx,tsx,css,json,yml,yaml}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

Use 2 spaces for indentation, no tabs

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{js,ts,jsx,tsx,css,json,yml,yaml,md}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

No trailing whitespace at the end of lines

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
apps/api/src/**/*.{controller,service}.ts

📄 CodeRabbit inference engine (.cursor/rules/06-api-structure.mdc)

Implement proper error handling in API modules

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
**/*.{css,scss,sass,less,js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/09-design-system.mdc)

**/*.{css,scss,sass,less,js,jsx,ts,tsx}: Primary color (#155EEF) should be used for main brand color in buttons, links, and accents
Error color (#F04438) should be used for error states and destructive actions
Success color (#12B76A) should be used for success states and confirmations
Warning color (#F79009) should be used for warnings and important notifications
Info color (#0BA5EC) should be used for informational elements

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)

**/*.{tsx,ts}: Use the translation wrapper component and useTranslation hook in components
Ensure all user-facing text is translatable

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{tsx,ts,json}

📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)

Support dynamic content with placeholders in translations

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{tsx,ts,jsx,js,vue,css,scss,less}

📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)

**/*.{tsx,ts,jsx,js,vue,css,scss,less}: Use the primary blue (#155EEF) for main UI elements, CTAs, and active states
Use red (#F04438) only for errors, warnings, and destructive actions
Use green (#12B76A) for success states and confirmations
Use orange (#F79009) for warning states and important notifications
Use blue (#0BA5EC) for informational elements
Primary buttons should be solid with the primary color
Secondary buttons should have a border with transparent or light background
Danger buttons should use the error color
Use consistent padding, border radius, and hover states for all buttons
Follow fixed button sizes based on their importance and context
Use consistent border radius (rounded-lg) for all cards
Apply light shadows (shadow-sm) for card elevation
Maintain consistent padding inside cards (p-4 or p-6)
Use subtle borders for card separation
Ensure proper spacing between card elements
Apply consistent styling to all form inputs
Use clear visual indicators for focus, hover, and error states in form elements
Apply proper spacing between elements using 8px, 16px, 24px increments
Ensure proper alignment of elements (left, center, or right)
Use responsive layouts that work across different device sizes
Maintain a minimum contrast ratio of 4.5:1 for text

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{tsx,ts,jsx,js,vue}

📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)

**/*.{tsx,ts,jsx,js,vue}: Include appropriate loading states for async actions in buttons
Group related form elements with appropriate spacing
Provide clear validation feedback for forms
Ensure proper labeling and accessibility for form elements
Ensure all interactive elements are keyboard accessible
Include appropriate ARIA attributes for complex components
Provide alternative text for images and icons
Support screen readers with semantic HTML elements

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/08-contributing-guidelines.mdc)

**/*.{ts,tsx,js,jsx}: Follow the TypeScript/JavaScript style guidelines
Ensure code is well-tested and documented

Files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
🧠 Learnings (2)
📚 Learning: 2025-11-25T03:04:12.836Z
Learnt from: CR
Repo: refly-ai/refly PR: 0
File: .cursor/rules/06-api-structure.mdc:0-0
Timestamp: 2025-11-25T03:04:12.836Z
Learning: Applies to apps/api/src/{auth,user,project,canvas,rag,knowledge,search,skill,share,code-artifact}/**/*.{controller,service}.ts : Use dependency injection for module dependencies in NestJS

Applied to files:

  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/provider.module.ts
📚 Learning: 2025-11-25T03:04:12.836Z
Learnt from: CR
Repo: refly-ai/refly PR: 0
File: .cursor/rules/06-api-structure.mdc:0-0
Timestamp: 2025-11-25T03:04:12.836Z
Learning: Applies to apps/api/src/{auth,user,project,canvas,rag,knowledge,search,skill,share,code-artifact}/**/*.{ts,tsx} : Follow NestJS module structure with controllers, services, and DTOs in dedicated module directories

Applied to files:

  • apps/api/src/modules/provider/provider.module.ts
🧬 Code graph analysis (1)
apps/api/src/modules/skill/skill.service.ts (2)
apps/api/src/modules/provider/auto-model-router.service.ts (1)
  • RoutingContext (59-96)
packages/utils/src/models.ts (1)
  • getModelSceneFromMode (10-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build / Build
🔇 Additional comments (11)
apps/api/src/modules/provider/provider.module.ts (1)

4-11: LGTM!

The dependency injection wiring correctly registers and exports AutoModelRoutingService, following NestJS patterns and aligning with the module structure guidelines.

apps/api/src/modules/provider/auto-model-router.service.ts (4)

18-96: LGTM! Well-designed routing interfaces.

The interface definitions are clean and well-documented:

  • RuleCondition provides extensible AND-logic matching
  • RoutingContext captures comprehensive context for routing decisions
  • RoutingStrategy enum clearly defines the fallback hierarchy
  • Good JSDoc comments explaining the purpose of each field

203-376: LGTM! Robust cache implementation.

The RuleCache class is well-designed with:

  • Proper lifecycle management via OnModuleDestroy
  • Reasonable TTL (3 minutes) with periodic background refresh
  • Resilient error handling (falls back to cached rules on fetch failure)
  • Deep rule comparison to detect changes
  • Proper timer cleanup with unref() to avoid blocking process exit

The intentional exclusion of timestamps from isRuleEqual() makes sense since metadata updates shouldn't trigger change notifications.


545-559: Reasoning model exclusion is documented.

The explicit exclusion of reasoning models from auto-routing (line 552) aligns with past review feedback. While this limitation was previously flagged, the design choice appears intentional for the current implementation.

As noted in the past review, consider making this behavior configurable in future iterations if reasoning models should be available for auto-routing in specific scenarios.

Based on learnings, the reasoning model exclusion has been previously discussed and is intentional in the current design.


565-715: LGTM! Clean routing implementation.

The routing methods are well-structured:

  • Clear separation of concerns across routing strategies
  • Proper error handling with non-blocking persistence
  • Defensive null checking in helper methods
  • Good logging for monitoring routing decisions
apps/api/src/modules/skill/skill.service.ts (5)

47-47: LGTM! Proper dependency injection.

The imports and constructor injection correctly introduce AutoModelRoutingService and RoutingContext, following NestJS patterns and the coding guidelines for dependency injection.

As per coding guidelines, dependency injection is properly used for module dependencies in NestJS.

Also applies to: 69-69, 109-109


487-502: LGTM! Well-constructed routing context.

The RoutingContext is properly constructed with:

  • Correct actionResultVersion calculation (incrementing from existing result or starting at 0)
  • Comprehensive context information for rule-based routing
  • Privacy-conscious handling of inputPrompt (noted as not stored)
  • All necessary fields for matching routing rules

526-557: LGTM! Proper surface vs. actual provider item handling.

The code correctly:

  • Distinguishes between surface model (param.modelItemId) for billing/UI and routed model (providerItem) for execution
  • Injects routedData metadata to track routing decisions
  • Preserves original identifiers for audit trails
  • Handles the isAutoModelRouted flag for downstream processing

833-836: LGTM! Cleaner model name resolution.

The refactored getModelNameForMode() method is much cleaner and more maintainable:

  • Uses getModelSceneFromMode() for consistent scene derivation
  • Replaces mode-specific branching with scene-based lookup
  • Aligns with the overall routing approach

510-524: The code already includes explicit validation with the condition if (originalModelProviderMap[primaryScene]) on line 518, which prevents routing when the primary scene is missing. No changes needed.

Likely an incorrect or invalid review comment.

apps/api/prisma/schema.prisma (1)

2224-2245: Schema allows nullable field in unique constraint, but code always assigns numeric values.

While actionResultVersion is marked as optional (Int?) in the Prisma schema, the routing service always assigns it a numeric value (0 or higher) and never sets it to NULL. The unique constraint @@unique([actionResultId, actionResultVersion]) therefore works as intended without the multiple-NULL issue initially flagged. No changes needed.

Likely an incorrect or invalid review comment.

@nettee nettee marked this pull request as draft December 22, 2025 11:20
@nettee nettee force-pushed the feat/920/auto-model-routing-rule branch from b499efb to 39dc7d9 Compare December 22, 2025 11:40
@nettee nettee marked this pull request as ready for review December 22, 2025 11:40
Copy link

@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

🧹 Nitpick comments (3)
apps/api/src/modules/provider/auto-model-router.service.ts (3)

106-131: Consider logging failed rule parsing for observability.

When safeParseJSON(rule.target) returns null (line 112), the code silently continues to the next rule. This could make debugging difficult if a rule is misconfigured.

Optional improvement
  route(
    rules: AutoModelRoutingRuleModel[],
    modelMap: Map<string, ProviderItemModel>,
  ): RuleRouteResult | null {
    for (const rule of rules) {
      if (this.matchRule(rule)) {
        const target = safeParseJSON(rule.target) as RoutingTarget;
        if (!target) {
+         this.logger.warn(`Failed to parse target for rule ${rule.ruleId}: ${rule.target}`);
          continue;
        }

        const selectedModel = this.selectModelFromTarget(target, modelMap);
        if (selectedModel) {
          return {
            providerItem: selectedModel,
            matchedRule: {
              ruleId: rule.ruleId,
              ruleName: rule.ruleName,
            },
          };
        }
      }
    }

    return null;
  }

Note: You'll need to pass a logger instance to the RuleRouter constructor.


285-300: Array comparison assumes consistent ordering.

The hasRulesChanged() method compares rules by index position (line 293-296), assuming the database returns rules in the same order. Since the query orders by [{ priority: 'desc' }, { ruleId: 'asc' }] (line 256), this should be stable, but the comparison is fragile to any changes in the ordering logic.

More robust comparison approach

Consider using a Set-based comparison or comparing rules by ruleId instead of array position to make the logic more resilient:

  private hasRulesChanged(
    oldRules: AutoModelRoutingRuleModel[],
    newRules: AutoModelRoutingRuleModel[],
  ): boolean {
    if (oldRules.length !== newRules.length) {
      return true;
    }

+   // Create a map of ruleId -> rule for efficient lookup
+   const oldRulesMap = new Map(oldRules.map(r => [r.ruleId, r]));
+   
+   for (const newRule of newRules) {
+     const oldRule = oldRulesMap.get(newRule.ruleId);
+     if (!oldRule || !this.isRuleEqual(oldRule, newRule)) {
+       return true;
+     }
+   }

-   for (let i = 0; i < oldRules.length; i++) {
-     if (!this.isRuleEqual(oldRules[i], newRules[i])) {
-       return true;
-     }
-   }

    return false;
  }

However, the current implementation is acceptable if rule ordering is guaranteed to be stable.


619-637: Non-blocking persistence could silently lose routing metadata.

Line 636 catches and logs errors from saveRoutingResultAsync() but doesn't propagate them. While this prevents routing failures from blocking execution, it means routing metadata might be lost without the caller knowing.

Consider one of these approaches:

  1. Add metrics/monitoring for failed routing result saves
  2. Use a dead-letter queue for failed saves
  3. At minimum, increment an error counter that can be monitored
private saveRoutingResult(...args) {
  this.saveRoutingResultAsync(...args)
    .catch((err) => {
      this.logger.warn('Failed to save routing result', err);
      // Add metric or monitoring here
      // this.metrics.incrementRoutingResultSaveFailures();
    });
}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b499efb and 39dc7d9.

📒 Files selected for processing (7)
  • apps/api/prisma/schema.prisma
  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/provider/provider.module.ts
  • apps/api/src/modules/skill/skill.service.ts
  • packages/skill-template/src/skills/agent.ts
  • packages/utils/src/id.ts
  • packages/utils/src/models.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/api/src/modules/provider/provider.module.ts
  • packages/skill-template/src/skills/agent.ts
  • packages/utils/src/models.ts
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{js,ts,jsx,tsx}: Always use optional chaining (?.) when accessing object properties
Always use nullish coalescing (??) or default values for potentially undefined values
Always check array existence before using array methods
Always validate object properties before destructuring
Always use single quotes for string literals in JavaScript/TypeScript code

**/*.{js,ts,jsx,tsx}: Use semicolons at the end of statements
Include spaces around operators (e.g., a + b instead of a+b)
Always use curly braces for control statements
Place opening braces on the same line as their statement

**/*.{js,ts,jsx,tsx}: Group import statements in order: React/framework libraries, third-party libraries, internal modules, relative path imports, type imports, style imports
Sort imports alphabetically within each import group
Leave a blank line between import groups
Extract complex logic into custom hooks
Use functional updates for state (e.g., setCount(prev => prev + 1))
Split complex state into multiple state variables rather than single large objects
Use useReducer for complex state logic instead of multiple useState calls

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{js,ts,tsx,jsx,py,java,cpp,c,cs,rb,go,rs,php,swift,kt,scala,r,m,mm,sql}

📄 CodeRabbit inference engine (.cursor/rules/00-language-priority.mdc)

**/*.{js,ts,tsx,jsx,py,java,cpp,c,cs,rb,go,rs,php,swift,kt,scala,r,m,mm,sql}: All code comments MUST be written in English
All variable names, function names, class names, and other identifiers MUST use English words
Comments should be concise and explain 'why' rather than 'what'
Use proper grammar and punctuation in comments
Keep comments up-to-date when code changes
Document complex logic, edge cases, and important implementation details
Use clear, descriptive names that indicate purpose
Avoid abbreviations unless they are universally understood

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/00-language-priority.mdc)

Use JSDoc style comments for functions and classes in JavaScript/TypeScript

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/01-code-style.mdc)

**/*.{js,jsx,ts,tsx}: Use single quotes for string literals in TypeScript/JavaScript
Always use optional chaining (?.) when accessing object properties in TypeScript/JavaScript
Always use nullish coalescing (??) or default values for potentially undefined values in TypeScript/JavaScript
Always check array existence before using array methods in TypeScript/JavaScript
Validate object properties before destructuring in TypeScript/JavaScript
Use ES6+ features like arrow functions, destructuring, and spread operators in TypeScript/JavaScript
Avoid magic numbers and strings - use named constants in TypeScript/JavaScript
Use async/await instead of raw promises for asynchronous code in TypeScript/JavaScript

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/03-typescript-guidelines.mdc)

**/*.{ts,tsx}: Avoid using any type whenever possible - use unknown type instead with proper type guards
Always define explicit return types for functions, especially for public APIs
Prefer extending existing types over creating entirely new types
Use TypeScript utility types (Partial<T>, Pick<T, K>, Omit<T, K>, Readonly<T>, Record<K, T>) to derive new types
Use union types and intersection types to combine existing types
Always import types explicitly using the import type syntax
Group type imports separately from value imports
Minimize creating local type aliases for imported types

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{js,ts,jsx,tsx,css,json}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

Maximum line length of 100 characters

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{js,ts,jsx,tsx,css,json,yml,yaml}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

Use 2 spaces for indentation, no tabs

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{js,ts,jsx,tsx,css,json,yml,yaml,md}

📄 CodeRabbit inference engine (.cursor/rules/04-code-formatting.mdc)

No trailing whitespace at the end of lines

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
apps/api/src/**/*.{controller,service}.ts

📄 CodeRabbit inference engine (.cursor/rules/06-api-structure.mdc)

Implement proper error handling in API modules

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{css,scss,sass,less,js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/09-design-system.mdc)

**/*.{css,scss,sass,less,js,jsx,ts,tsx}: Primary color (#155EEF) should be used for main brand color in buttons, links, and accents
Error color (#F04438) should be used for error states and destructive actions
Success color (#12B76A) should be used for success states and confirmations
Warning color (#F79009) should be used for warnings and important notifications
Info color (#0BA5EC) should be used for informational elements

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)

**/*.{tsx,ts}: Use the translation wrapper component and useTranslation hook in components
Ensure all user-facing text is translatable

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{tsx,ts,json}

📄 CodeRabbit inference engine (.cursor/rules/09-i18n-guidelines.mdc)

Support dynamic content with placeholders in translations

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{tsx,ts,jsx,js,vue,css,scss,less}

📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)

**/*.{tsx,ts,jsx,js,vue,css,scss,less}: Use the primary blue (#155EEF) for main UI elements, CTAs, and active states
Use red (#F04438) only for errors, warnings, and destructive actions
Use green (#12B76A) for success states and confirmations
Use orange (#F79009) for warning states and important notifications
Use blue (#0BA5EC) for informational elements
Primary buttons should be solid with the primary color
Secondary buttons should have a border with transparent or light background
Danger buttons should use the error color
Use consistent padding, border radius, and hover states for all buttons
Follow fixed button sizes based on their importance and context
Use consistent border radius (rounded-lg) for all cards
Apply light shadows (shadow-sm) for card elevation
Maintain consistent padding inside cards (p-4 or p-6)
Use subtle borders for card separation
Ensure proper spacing between card elements
Apply consistent styling to all form inputs
Use clear visual indicators for focus, hover, and error states in form elements
Apply proper spacing between elements using 8px, 16px, 24px increments
Ensure proper alignment of elements (left, center, or right)
Use responsive layouts that work across different device sizes
Maintain a minimum contrast ratio of 4.5:1 for text

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{tsx,ts,jsx,js,vue}

📄 CodeRabbit inference engine (.cursor/rules/11-ui-design-patterns.mdc)

**/*.{tsx,ts,jsx,js,vue}: Include appropriate loading states for async actions in buttons
Group related form elements with appropriate spacing
Provide clear validation feedback for forms
Ensure proper labeling and accessibility for form elements
Ensure all interactive elements are keyboard accessible
Include appropriate ARIA attributes for complex components
Provide alternative text for images and icons
Support screen readers with semantic HTML elements

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/08-contributing-guidelines.mdc)

**/*.{ts,tsx,js,jsx}: Follow the TypeScript/JavaScript style guidelines
Ensure code is well-tested and documented

Files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • packages/utils/src/id.ts
  • apps/api/src/modules/skill/skill.service.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: nettee
Repo: refly-ai/refly PR: 1911
File: apps/api/src/modules/provider/auto-model-router.service.ts:545-559
Timestamp: 2025-12-22T09:48:22.120Z
Learning: In apps/api/src/modules/provider/auto-model-router.service.ts, reasoning models (where config.capabilities.reasoning === true) are intentionally excluded from auto-routing in the buildModelMap method. This is a deliberate design decision.
📚 Learning: 2025-12-22T09:48:22.120Z
Learnt from: nettee
Repo: refly-ai/refly PR: 1911
File: apps/api/src/modules/provider/auto-model-router.service.ts:545-559
Timestamp: 2025-12-22T09:48:22.120Z
Learning: In apps/api/src/modules/provider/auto-model-router.service.ts, reasoning models (where config.capabilities.reasoning === true) are intentionally excluded from auto-routing in the buildModelMap method. This is a deliberate design decision.

Applied to files:

  • apps/api/src/modules/provider/auto-model-router.service.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/prisma/schema.prisma
📚 Learning: 2025-11-25T03:04:12.836Z
Learnt from: CR
Repo: refly-ai/refly PR: 0
File: .cursor/rules/06-api-structure.mdc:0-0
Timestamp: 2025-11-25T03:04:12.836Z
Learning: Applies to apps/api/src/{auth,user,project,canvas,rag,knowledge,search,skill,share,code-artifact}/**/*.{controller,service}.ts : Use dependency injection for module dependencies in NestJS

Applied to files:

  • apps/api/src/modules/skill/skill.service.ts
🧬 Code graph analysis (2)
apps/api/src/modules/provider/auto-model-router.service.ts (4)
apps/api/src/modules/tool/utils/token.ts (1)
  • safeParseJSON (371-377)
packages/utils/src/auto-model.ts (3)
  • isAutoModel (92-112)
  • selectAutoModel (41-48)
  • AUTO_MODEL_ROUTING_PRIORITY (14-20)
packages/utils/src/id.ts (1)
  • genRoutingResultID (329-331)
packages/utils/src/models.ts (1)
  • getModelSceneFromMode (10-14)
apps/api/src/modules/skill/skill.service.ts (2)
apps/api/src/modules/provider/auto-model-router.service.ts (1)
  • RoutingContext (60-97)
packages/utils/src/models.ts (1)
  • getModelSceneFromMode (10-14)
🔇 Additional comments (10)
packages/utils/src/id.ts (1)

54-55: LGTM! ID generators follow established patterns.

The new routing-related ID prefixes and generator functions are consistent with existing conventions in the file and align with the routing system requirements.

Also applies to: 329-335

apps/api/src/modules/provider/auto-model-router.service.ts (3)

1-97: Well-structured type definitions with clear separation of concerns.

The interface definitions are comprehensive and well-documented. Good attention to privacy in the inputPrompt field documentation noting it's not stored.


412-500: Multi-tier routing strategy is well-structured and resilient.

The 5-tier fallback approach (rule-based → tool-based → random → priority → first available) provides excellent resilience. Each tier persists its routing decision for observability, which is valuable for debugging and analytics.


545-582: Tool-based routing scope is clearly defined.

The explicit checks for mode === 'node_agent' and scene === 'agent' clearly define when tool-based routing applies. The early returns make the logic easy to follow.

apps/api/prisma/schema.prisma (2)

2186-2214: Well-designed routing rule schema with optimal indexes.

The AutoModelRoutingRule model has:

  • Proper field types for JSON storage (condition, target)
  • Sensible defaults (priority = 0, enabled = true)
  • Optimal index for query pattern: [scene, enabled, priority(sort: Desc), ruleId] matches the query in RuleCache.fetchAndCache()

2216-2248: Comprehensive routing result tracking with proper constraints.

The AutoModelRoutingResult model captures all necessary routing metadata:

  • Links to action results via [actionResultId, actionResultVersion] unique constraint
  • Stores matched rule info for observability
  • Records both original and selected model IDs for analytics
  • Efficient query support with [userId, createdAt(sort: Desc)] index
apps/api/src/modules/skill/skill.service.ts (4)

47-47: Clean integration of routing service via dependency injection.

The new dependencies (getModelSceneFromMode, AutoModelRoutingService, RoutingContext) are properly imported and injected following NestJS patterns.

Based on learnings: Uses dependency injection for module dependencies in NestJS.

Also applies to: 69-69, 109-109


487-502: RoutingContext now includes comprehensive metadata for rule-based routing.

The context now captures:

  • actionResultId and actionResultVersion for associating routing decisions with executions
  • mode for scene derivation
  • inputPrompt for potential regex matching (noted as not stored for privacy)
  • toolsets for tool-based routing

This rich context enables sophisticated routing rules while respecting privacy constraints.


560-572: Routing metadata injection enables tracking without schema changes.

Injecting routedData into the config when routing occurs (line 561-571) is a clean way to track routing decisions without modifying the database schema. This metadata can be used for billing, analytics, and debugging.


848-851: Scene derivation is now consistent across routing and execution.

Using getModelSceneFromMode() from the utility module ensures consistent scene mapping throughout the routing and execution flow. This replaces any local derivation logic and reduces the risk of inconsistencies.

- Introduce AutoModelRoutingRule and AutoModelRoutingResult database models
- Refactor AutoModelRouter into AutoModelRoutingService with NestJS integration
- Implement rule-based routing with in-memory caching and periodic refresh
- Support multi-tier routing strategy (Rule-based, Tool-based, legacy strategies)
- Add asynchronous tracking for routing decisions and matched rules
- Integrate the new routing service into SkillService
@nettee nettee force-pushed the feat/920/auto-model-routing-rule branch from 39dc7d9 to eefb838 Compare December 22, 2025 14:58
@nettee nettee merged commit 177f88a into main Dec 23, 2025
2 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.

3 participants