Skip to content

Conversation

@nettee
Copy link
Contributor

@nettee nettee commented Dec 17, 2025

Summary

This PR adds tool-based auto model routing for the Auto model. When running in node_agent mode, the router now checks which tools are being used and routes to different models based on whether target tools are present. This allows us to use specific models for certain tool combinations.

Changes

  • Added tool-based routing logic to AutoModelRouter that checks for target tools and routes to matched/unmatched models
  • Extended RouterContext with scene and toolsets fields to support routing decisions
  • Added getToolBasedRoutingConfig() utility to read routing config from environment variables
  • Updated SkillService to pass scene and toolsets to the router
  • Updated WorkflowService to set mode: 'node_agent' when invoking skills

Summary by CodeRabbit

  • New Features
    • Tool-based routing: models can be selected automatically based on required tools and scene context.
  • Improvements
    • Routing context now includes scene and toolset information for smarter selection.
    • Tool-based routing is configurable and logs routing decisions and fallbacks for better visibility.
    • Invoke-skill requests now include a new mode ("node_agent") in their payload.

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 17, 2025

Walkthrough

Adds tool-based routing: RouterContext gains scene and toolsets, AutoModelRouter reads env-configured tool routing via getToolBasedRoutingConfig(), attempts tool matching in tryToolBasedRouting() before existing routing, and skill/workflow services propagate scene/toolsets and set mode 'node_agent'.

Changes

Cohort / File(s) Summary
Auto-model router core logic
apps/api/src/modules/provider/auto-model-router.service.ts
Added scene?: string and toolsets?: GenericToolset[] to RouterContext; imported GenericToolset and getToolBasedRoutingConfig; added private tryToolBasedRouting(modelMap) and invoked it at the start of findAvailableModel() to apply tool-based routing with logging and fallback handling.
Routing context propagation
apps/api/src/modules/skill/skill.service.ts, <br>apps/api/src/modules/workflow/workflow.service.ts`
prepareInvokeSkillJobData now includes scene (from mode) and toolsets in router context; invokeSkillTask payload includes mode: 'node_agent'.
Tool-based routing configuration utilities
packages/utils/src/auto-model.ts
Added ToolBasedRoutingConfig interface and getToolBasedRoutingConfig() which parses environment variables for enabled flag, target tools list, and matched/unmatched model IDs; exported both.

Sequence Diagram(s)

sequenceDiagram
    participant Workflow as WorkflowService
    participant Skill as SkillService
    participant Router as AutoModelRouter
    participant Env as Env (utils.getToolBasedRoutingConfig)

    Workflow->>Skill: invokeSkillTask(mode:'node_agent', ...)
    Skill->>Skill: prepareInvokeSkillJobData(scene, toolsets)
    Skill->>Router: route(RouterContext{scene, toolsets, ...})
    Router->>Env: getToolBasedRoutingConfig()
    Env-->>Router: ToolBasedRoutingConfig
    Router->>Router: tryToolBasedRouting(modelMap, toolsets)
    alt tools matched
        Router->>Router: select matchedModelId -> provider model
    else tools unmatched
        Router->>Router: select unmatchedModelId or fallback to existing priorities
    end
    Router-->>Skill: ProviderItemModel (selected)
    Skill-->>Workflow: proceed with routed provider
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Extra attention:
    • tryToolBasedRouting() correctness and edge cases (empty/partial toolsets)
    • Env parsing in getToolBasedRoutingConfig() (trimming, empty entries, boolean parsing)
    • Interaction with existing routing fallbacks and logging messages

Possibly related PRs

Suggested reviewers

  • mrcfps
  • pftom
  • lefarcen

Poem

🐰 I hopped through configs, sniffed the tools,

Matched the lists and read the rules,
Scene in paw, I chose the best—
Routed models pass the test!
A tiny hop, a clever tweak, hooray! 🎉

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main feature: implementing tool-based auto model routing, which aligns with all key changes across multiple files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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/auto-model-routing-tool-based

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ad9fbc and 1c13786.

📒 Files selected for processing (4)
  • apps/api/src/modules/provider/auto-model-router.service.ts (5 hunks)
  • apps/api/src/modules/skill/skill.service.ts (1 hunks)
  • apps/api/src/modules/workflow/workflow.service.ts (1 hunks)
  • packages/utils/src/auto-model.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/workflow/workflow.service.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
**/*.{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
**/*.{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
**/*.{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
**/*.{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
**/*.{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
**/*.{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
**/*.{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
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
**/*.{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
**/*.{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
**/*.{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
**/*.{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
**/*.{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
**/*.{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
🧬 Code graph analysis (1)
apps/api/src/modules/provider/auto-model-router.service.ts (2)
packages/openapi-schema/src/types.gen.ts (1)
  • GenericToolset (6983-7016)
packages/utils/src/auto-model.ts (1)
  • getToolBasedRoutingConfig (64-85)
⏰ 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 (5)
apps/api/src/modules/provider/auto-model-router.service.ts (5)

3-3: LGTM!

The new imports are correctly added to support tool-based routing functionality.

Also applies to: 9-9


29-39: LGTM!

The RouterContext interface is correctly extended with optional fields for tool-based routing. The optional nature ensures backward compatibility, and the JSDoc comments clearly explain the purpose of each field.


73-111: LGTM!

The priority system is well-structured with tool-based routing appropriately positioned as the highest priority. The fallback logic ensures robustness when tool-based routing is not applicable.


146-153: LGTM!

The guard clauses correctly validate that tool-based routing should be applied, checking both the scene and the configuration. Early returns keep the code clean and efficient.


159-179: LGTM!

The model selection logic is sound: it checks for target tool matches, selects the appropriate model ID, validates availability, and provides appropriate logging for both success and fallback scenarios.


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: 2

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 493e505 and 7ad9fbc.

📒 Files selected for processing (4)
  • apps/api/src/modules/provider/auto-model-router.service.ts (6 hunks)
  • apps/api/src/modules/skill/skill.service.ts (1 hunks)
  • apps/api/src/modules/workflow/workflow.service.ts (1 hunks)
  • packages/utils/src/auto-model.ts (1 hunks)
🧰 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/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.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:

  • apps/api/src/modules/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.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:

  • apps/api/src/modules/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.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:

  • apps/api/src/modules/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.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:

  • apps/api/src/modules/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.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:

  • apps/api/src/modules/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.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:

  • apps/api/src/modules/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.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:

  • apps/api/src/modules/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.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/workflow/workflow.service.ts
  • 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/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.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:

  • apps/api/src/modules/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.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:

  • apps/api/src/modules/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.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:

  • apps/api/src/modules/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.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:

  • apps/api/src/modules/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.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:

  • apps/api/src/modules/workflow/workflow.service.ts
  • packages/utils/src/auto-model.ts
  • apps/api/src/modules/skill/skill.service.ts
  • apps/api/src/modules/provider/auto-model-router.service.ts
🧬 Code graph analysis (1)
apps/api/src/modules/provider/auto-model-router.service.ts (2)
packages/openapi-schema/src/types.gen.ts (1)
  • GenericToolset (6983-7016)
packages/utils/src/auto-model.ts (1)
  • getToolBasedRoutingConfig (64-85)
⏰ 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 (6)
apps/api/src/modules/skill/skill.service.ts (1)

487-492: LGTM! RouterContext properly extended with scene and toolsets.

The routing context now includes the necessary fields for tool-based routing decisions.

packages/utils/src/auto-model.ts (2)

50-58: LGTM! Well-defined interface for tool-based routing configuration.


64-85: LGTM! Environment variable parsing is clean and safe.

The function properly handles missing or malformed environment variables with appropriate defaults.

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

29-39: LGTM! RouterContext properly extended for tool-based routing.

The new fields are well-documented and appropriately typed.


117-121: LGTM! Tool-based routing properly prioritized.

The tool-based routing is correctly positioned as the highest priority in the routing strategy.

apps/api/src/modules/workflow/workflow.service.ts (1)

338-338: LGTM. Hardcoded 'node_agent' mode is the correct pattern for workflow skill invocations.

Workflow skill invocations are designed to consistently use 'node_agent' mode for tool-based routing. The ResponseNodeMeta type doesn't include mode configuration, and this is the only place in workflow.service.ts where InvokeSkillRequest is created, confirming this as the intended behavior across all workflow executions.

Comment on lines 60 to 68
// Debug log: print routing context
const toolKeysSet = new Set(
this.context.toolsets?.map((t) => t.toolset?.key).filter((key): key is string => !!key) ?? [],
);
const toolKeys = Array.from(toolKeysSet).join(', ') || 'none';
this.logger.debug(
`[AutoModelRouter] Routing context - Scene: ${this.context.scene ?? 'undefined'}, ` +
`Tools: [${toolKeys}], UserId: ${this.context.userId}`,
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix tool key extraction logic to handle builtin toolsets.

The tool key extraction using t.toolset?.key will fail for builtin toolsets (like 'execute_code', 'read_file', 'get_time' defined in skill.service.ts lines 75-79) because they don't have the toolset property populated.

Apply this diff to use a fallback chain:

-    const toolKeysSet = new Set(
-      this.context.toolsets?.map((t) => t.toolset?.key).filter((key): key is string => !!key) ?? [],
-    );
+    const toolKeysSet = new Set(
+      this.context.toolsets?.map((t) => t.toolset?.key ?? t.name ?? t.id).filter((key): key is string => !!key) ?? [],
+    );
🤖 Prompt for AI Agents
In apps/api/src/modules/provider/auto-model-router.service.ts around lines
60-68, the current extraction uses t.toolset?.key which breaks for builtin
toolset entries that do not populate the toolset property; change the mapping to
fall back to a top-level key (e.g., t.toolset?.key ?? t.key) before filtering
and building the Set so builtin entries are included, keep the same
string-filtering predicate to ensure only defined strings are collected, and
leave the rest of the debug log logic unchanged.

Comment on lines +155 to +180
private tryToolBasedRouting(modelMap: Map<string, ProviderItemModel>): ProviderItemModel | null {
if (this.context.scene !== 'node_agent') {
return null;
}

const config = getToolBasedRoutingConfig();
if (!config.enabled) {
return null;
}

const toolKeysSet = new Set(
this.context.toolsets?.map((t) => t.toolset?.key).filter((key): key is string => !!key) ?? [],
);

const hasTargetTool = config.targetTools.some((targetTool) => toolKeysSet.has(targetTool));

const targetModelId = hasTargetTool ? config.matchedModelId : config.unmatchedModelId;
if (!targetModelId) {
return null;
}

const targetModel = modelMap.get(targetModelId);

if (!targetModel) {
this.logger.warn(
`[AutoModelRouter] Tool-based routing fallback: target model '${targetModelId}' not available for user ${this.context.userId}`,
);
return null;
}

this.logger.log(
`[AutoModelRouter] Tool-based routing applied: tools ${hasTargetTool ? 'matched' : 'unmatched'}, ` +
`routing to ${targetModel.name} (modelId: ${targetModelId})`,
);
return targetModel;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

❓ Verification inconclusive

Script executed:

# Find the GenericToolset or toolset type definition
fd -e ts -e tsx | xargs grep -l "GenericToolset\|interface.*[Tt]oolset" | head -20

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

packages/skill-template/src/prompts/copilot-agent.ts
packages/skill-template/src/base.ts
packages/stores/src/types/common.ts
packages/stores/src/stores/launchpad.ts
packages/stores/src/stores/front-page.ts
packages/request/src/requests/types.gen.ts
packages/openapi-schema/src/types.gen.ts
packages/openapi-schema/src/schemas.gen.ts
packages/canvas-common/src/utils.ts
packages/canvas-common/src/types.ts
packages/canvas-common/src/workflow-plan.ts
packages/canvas-common/src/tools.ts
packages/canvas-common/src/tools.test.ts
packages/canvas-common/src/data.ts
packages/canvas-common/src/data.test.ts
packages/ai-workspace-common/src/requests/types.gen.ts
packages/ai-workspace-common/src/hooks/canvas/use-invoke-action.ts
packages/ai-workspace-common/src/hooks/use-toolset-definition.ts
packages/ai-workspace-common/src/hooks/canvas/use-agent-node-management.ts
packages/ai-workspace-common/src/components/workflow-list/toolset-popover.tsx

Script executed:

# Check the context type definition to understand toolsets structure
rg -A 20 "context.*toolsets" apps/api/src/modules/provider/auto-model-router.service.ts | head -40

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

      this.context.toolsets?.map((t) => t.toolset?.key).filter((key): key is string => !!key) ?? [],
    );
    const toolKeys = Array.from(toolKeysSet).join(', ') || 'none';
    this.logger.debug(
      `[AutoModelRouter] Routing context - Scene: ${this.context.scene ?? 'undefined'}, ` +
        `Tools: [${toolKeys}], UserId: ${this.context.userId}`,
    );

    if (!isAutoModel(chatItem.config)) {
      return chatItem;
    }

    const routedItem = this.findAvailableModel();

    this.logger.log(
      `Routed auto model to ${routedItem.name} (itemId: ${routedItem.itemId}) for user ${this.context.userId}`,
    );

    return routedItem;
  }

--
      this.context.toolsets?.map((t) => t.toolset?.key).filter((key): key is string => !!key) ?? [],
    );

    const hasTargetTool = config.targetTools.some((targetTool) => toolKeysSet.has(targetTool));

    const targetModelId = hasTargetTool ? config.matchedModelId : config.unmatchedModelId;
    if (!targetModelId) {
      return null;
    }

    const targetModel = modelMap.get(targetModelId);

    if (!targetModel) {
      this.logger.warn(
        `[AutoModelRouter] Tool-based routing fallback: target model '${targetModelId}' not available for user ${this.context.userId}`,
      );
      return null;
    }

Script executed:

# Look for other usages of toolset key extraction in the codebase
rg "toolset\?.key|t\.toolset\?" apps/api/src --type ts -B 2 -A 2

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

apps/api/src/modules/tool/tool.dto.ts-
apps/api/src/modules/tool/tool.dto.ts-  return toolsets.map((toolset) => {
apps/api/src/modules/tool/tool.dto.ts:    if (!toolset.toolset?.key) {
apps/api/src/modules/tool/tool.dto.ts-      return toolset;
apps/api/src/modules/tool/tool.dto.ts-    }
--
apps/api/src/modules/tool/tool.service.ts-
apps/api/src/modules/tool/tool.service.ts-    // 2. Get installed toolset keys for filtering
apps/api/src/modules/tool/tool.service.ts:    const installedKeys = new Set(populatedTools.map((t) => t.toolset?.key).filter(Boolean));
apps/api/src/modules/tool/tool.service.ts-
apps/api/src/modules/tool/tool.service.ts-    // 3. Get all external OAuth tools from inventory that are not installed
--
apps/api/src/modules/tool/tool.service.ts-    const authorizedItems: UserTool[] = populatedTools.map((tool) => ({
apps/api/src/modules/tool/tool.service.ts-      toolsetId: tool.id,
apps/api/src/modules/tool/tool.service.ts:      key: tool.toolset?.key || tool.id,
apps/api/src/modules/tool/tool.service.ts-      name: tool.name,
apps/api/src/modules/tool/tool.service.ts-      description: (tool.toolset?.definition?.descriptionDict?.en as string) || tool.name,
--
apps/api/src/modules/tool/tool.service.ts-    toolset: GenericToolset,
apps/api/src/modules/tool/tool.service.ts-  ): Promise<GenericToolset | null> {
apps/api/src/modules/tool/tool.service.ts:    const integrationId = toolset.toolset?.key;
apps/api/src/modules/tool/tool.service.ts-    const activeConnection = await this.prisma.composioConnection.findFirst({
apps/api/src/modules/tool/tool.service.ts-      where: {
--
apps/api/src/modules/tool/composio/composio.service.ts-    const structuredTools: StructuredToolInterface[] = [];
apps/api/src/modules/tool/composio/composio.service.ts-    for (const toolset of toolsets) {
apps/api/src/modules/tool/composio/composio.service.ts:      const integrationId = toolset.toolset?.key;
apps/api/src/modules/tool/composio/composio.service.ts-      if (!integrationId) {
apps/api/src/modules/tool/composio/composio.service.ts-        continue;
--
apps/api/src/modules/tool/composio/composio.service.ts-          creditCost,
apps/api/src/modules/tool/composio/composio.service.ts-          toolsetType: toolset.type,
apps/api/src/modules/tool/composio/composio.service.ts:          toolsetKey: toolset.toolset?.key ?? '',
apps/api/src/modules/tool/composio/composio.service.ts-          toolsetName: inventory?.name ?? toolset.name,
apps/api/src/modules/tool/composio/composio.service.ts-        };
--
apps/api/src/modules/provider/auto-model-router.service.ts-    // Debug log: print routing context
apps/api/src/modules/provider/auto-model-router.service.ts-    const toolKeysSet = new Set(
apps/api/src/modules/provider/auto-model-router.service.ts:      this.context.toolsets?.map((t) => t.toolset?.key).filter((key): key is string => !!key) ?? [],
apps/api/src/modules/provider/auto-model-router.service.ts-    );
apps/api/src/modules/provider/auto-model-router.service.ts-    const toolKeys = Array.from(toolKeysSet).join(', ') || 'none';
--
apps/api/src/modules/provider/auto-model-router.service.ts-
apps/api/src/modules/provider/auto-model-router.service.ts-    const toolKeysSet = new Set(
apps/api/src/modules/provider/auto-model-router.service.ts:      this.context.toolsets?.map((t) => t.toolset?.key).filter((key): key is string => !!key) ?? [],
apps/api/src/modules/provider/auto-model-router.service.ts-    );
apps/api/src/modules/provider/auto-model-router.service.ts-
--
apps/api/src/modules/pilot/prompt/formatter.ts-    const key =
apps/api/src/modules/pilot/prompt/formatter.ts-      genericToolset.type === 'regular'
apps/api/src/modules/pilot/prompt/formatter.ts:        ? (genericToolset.toolset?.key ?? 'unknown-key')
apps/api/src/modules/pilot/prompt/formatter.ts-        : (genericToolset.name ?? 'unknown-name');
apps/api/src/modules/pilot/prompt/formatter.ts-
apps/api/src/modules/pilot/prompt/formatter.ts-    const description =
apps/api/src/modules/pilot/prompt/formatter.ts:      genericToolset.toolset?.definition?.descriptionDict?.en ?? 'No description available';
apps/api/src/modules/pilot/prompt/formatter.ts-
apps/api/src/modules/pilot/prompt/formatter.ts:    const tools = genericToolset.toolset?.definition?.tools ?? [];
apps/api/src/modules/pilot/prompt/formatter.ts-    const toolList = tools
apps/api/src/modules/pilot/prompt/formatter.ts-      .map((tool) => {
--
apps/api/src/modules/pilot/pilot-engine.ts-          (category) =>
apps/api/src/modules/pilot/pilot-engine.ts-            toolset.name?.toLowerCase().includes(category.toLowerCase()) ||
apps/api/src/modules/pilot/pilot-engine.ts:            toolset.toolset?.definition?.tools?.some((tool) =>
apps/api/src/modules/pilot/pilot-engine.ts-              tool.name?.toLowerCase().includes(category.toLowerCase()),
apps/api/src/modules/pilot/pilot-engine.ts-            ),
--
apps/api/src/modules/pilot/pilot-engine.ts-      .map(
apps/api/src/modules/pilot/pilot-engine.ts-        (toolset) =>
apps/api/src/modules/pilot/pilot-engine.ts:          `${toolset.name}: ${toolset.toolset?.definition?.descriptionDict?.en || 'No description available'}`,
apps/api/src/modules/pilot/pilot-engine.ts-      )
apps/api/src/modules/pilot/pilot-engine.ts-      .join('\n');
--
apps/api/src/modules/pilot/intent-analysis.service.ts-      .map(
apps/api/src/modules/pilot/intent-analysis.service.ts-        (toolset) =>
apps/api/src/modules/pilot/intent-analysis.service.ts:          `${toolset.name}: ${toolset.toolset?.definition?.descriptionDict?.en || 'No description available'}`,
apps/api/src/modules/pilot/intent-analysis.service.ts-      )
apps/api/src/modules/pilot/intent-analysis.service.ts-      .join('\n');

Script executed:

# Find the getToolBasedRoutingConfig function
rg -A 10 "getToolBasedRoutingConfig" apps/api/src --type ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

apps/api/src/modules/provider/auto-model-router.service.ts:  getToolBasedRoutingConfig,
apps/api/src/modules/provider/auto-model-router.service.ts-} from '@refly/utils';
apps/api/src/modules/provider/auto-model-router.service.ts-import { ProviderItemNotFoundError } from '@refly/errors';
apps/api/src/modules/provider/auto-model-router.service.ts-
apps/api/src/modules/provider/auto-model-router.service.ts-/**
apps/api/src/modules/provider/auto-model-router.service.ts- * Context for Auto model routing
apps/api/src/modules/provider/auto-model-router.service.ts- * Contains all the data needed for routing decisions
apps/api/src/modules/provider/auto-model-router.service.ts- */
apps/api/src/modules/provider/auto-model-router.service.ts-export interface RouterContext {
apps/api/src/modules/provider/auto-model-router.service.ts-  /**
apps/api/src/modules/provider/auto-model-router.service.ts-   * LLM provider items available for the user
--
apps/api/src/modules/provider/auto-model-router.service.ts:    const config = getToolBasedRoutingConfig();
apps/api/src/modules/provider/auto-model-router.service.ts-    if (!config.enabled) {
apps/api/src/modules/provider/auto-model-router.service.ts-      return null;
apps/api/src/modules/provider/auto-model-router.service.ts-    }
apps/api/src/modules/provider/auto-model-router.service.ts-
apps/api/src/modules/provider/auto-model-router.service.ts-    const toolKeysSet = new Set(
apps/api/src/modules/provider/auto-model-router.service.ts-      this.context.toolsets?.map((t) => t.toolset?.key).filter((key): key is string => !!key) ?? [],
apps/api/src/modules/provider/auto-model-router.service.ts-    );
apps/api/src/modules/provider/auto-model-router.service.ts-
apps/api/src/modules/provider/auto-model-router.service.ts-    const hasTargetTool = config.targetTools.some((targetTool) => toolKeysSet.has(targetTool));
apps/api/src/modules/provider/auto-model-router.service.ts-

Script executed:

# Check for builtin toolset handling patterns
rg -i "builtin.*toolset|toolset.*builtin" apps/api/src --type ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

apps/api/src/modules/tool/tool.service.ts:  BuiltinToolset,
apps/api/src/modules/tool/tool.service.ts:  BuiltinToolsetDefinition,
apps/api/src/modules/tool/tool.service.ts:  builtinToolsetInventory,
apps/api/src/modules/tool/tool.service.ts:   * Load toolset inventory from sources (builtin + external)
apps/api/src/modules/tool/tool.service.ts:    const builtinInventory = Object.values(builtinToolsetInventory).map((toolset) => ({
apps/api/src/modules/tool/tool.service.ts:  listBuiltinTools(): GenericToolset[] {
apps/api/src/modules/tool/tool.service.ts:    return Object.values(builtinToolsetInventory)
apps/api/src/modules/tool/tool.service.ts:          toolsetId: 'builtin',
apps/api/src/modules/tool/tool.service.ts:      const { type, id, selectedTools, builtin } = selectedToolset;
apps/api/src/modules/tool/tool.service.ts:      if (toolset.builtin) {
apps/api/src/modules/tool/tool.service.ts:    const { name, toolset: toolsetInstance, builtin } = toolset;
apps/api/src/modules/tool/tool.service.ts:    // Builtin toolset does not need to be imported
apps/api/src/modules/tool/tool.service.ts:    const builtinKeys = toolsets
apps/api/src/modules/tool/tool.service.ts:      .filter((t) => t.type === ToolsetType.REGULAR && t.builtin)
apps/api/src/modules/tool/tool.service.ts:      builtinTools = this.instantiateBuiltinToolsets(user, engine, builtinKeys);
apps/api/src/modules/tool/tool.service.ts:    const regularToolsets = toolsets.filter((t) => t.type === ToolsetType.REGULAR && !t.builtin);
apps/api/src/modules/tool/tool.service.ts:   * Instantiate builtin toolsets into structured tools.
apps/api/src/modules/tool/tool.service.ts:  private instantiateBuiltinToolsets(
apps/api/src/modules/tool/tool.service.ts:    const toolsetInstance = new BuiltinToolset({
apps/api/src/modules/tool/tool.service.ts:    return BuiltinToolsetDefinition.tools
apps/api/src/modules/tool/tool.service.ts:            name: `${BuiltinToolsetDefinition.key}_${tool.name}`,
apps/api/src/modules/skill/skill.service.ts: * Fixed builtin toolsets that are always available for node_agent mode.
apps/api/src/modules/skill/skill.service.ts: * Note: IDs must match BuiltinToolsetDefinition.tools[].name for instantiateBuiltinToolsets to work.
apps/api/src/modules/skill/skill.service.ts:const FIXED_BUILTIN_TOOLSETS: GenericToolset[] = [
apps/api/src/modules/skill/skill.service.ts:   * Append fixed builtin toolsets to user-selected toolsets.
apps/api/src/modules/skill/skill.service.ts:    const toolsetsToAppend = FIXED_BUILTIN_TOOLSETS.filter((t) => !existingIds.has(t.id));
apps/api/src/modules/skill/skill.service.ts:      // Append fixed builtin toolsets for non-copilot mode

Script executed:

# Find GenericToolset type definition to understand toolset structure
rg -A 20 "type GenericToolset|interface GenericToolset" apps/api/src --type ts | head -50

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0


Script executed:

# Look at the formatter.ts logic more closely to understand the pattern
rg -B 5 -A 10 "genericToolset.type === 'regular'" apps/api/src --type ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

apps/api/src/modules/pilot/prompt/formatter.ts-  // Handle GenericToolset type
apps/api/src/modules/pilot/prompt/formatter.ts-  // TODO: optimize formatter for MCP tools
apps/api/src/modules/pilot/prompt/formatter.ts-  if ('type' in toolset) {
apps/api/src/modules/pilot/prompt/formatter.ts-    const genericToolset = toolset as GenericToolset;
apps/api/src/modules/pilot/prompt/formatter.ts-    const key =
apps/api/src/modules/pilot/prompt/formatter.ts:      genericToolset.type === 'regular'
apps/api/src/modules/pilot/prompt/formatter.ts-        ? (genericToolset.toolset?.key ?? 'unknown-key')
apps/api/src/modules/pilot/prompt/formatter.ts-        : (genericToolset.name ?? 'unknown-name');
apps/api/src/modules/pilot/prompt/formatter.ts-
apps/api/src/modules/pilot/prompt/formatter.ts-    const description =
apps/api/src/modules/pilot/prompt/formatter.ts-      genericToolset.toolset?.definition?.descriptionDict?.en ?? 'No description available';
apps/api/src/modules/pilot/prompt/formatter.ts-
apps/api/src/modules/pilot/prompt/formatter.ts-    const tools = genericToolset.toolset?.definition?.tools ?? [];
apps/api/src/modules/pilot/prompt/formatter.ts-    const toolList = tools
apps/api/src/modules/pilot/prompt/formatter.ts-      .map((tool) => {
apps/api/src/modules/pilot/prompt/formatter.ts-        const toolDescription =

Script executed:

# Check if toolsets have a 'builtin' or 'type' property that affects key access
rg -B 5 -A 5 "toolset.builtin|t.builtin|t.type" apps/api/src/modules/provider --type ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 1


Script executed:

# Search for BuiltinToolsetDefinition to understand builtin toolset structure
rg -A 15 "BuiltinToolsetDefinition\s*=" apps/api/src --type ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 1


Script executed:

# Search for GenericToolset more broadly (could be imported from utils or types)
rg "GenericToolset" apps/api/src/modules/provider/auto-model-router.service.ts -B 5 -A 5

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

import { Logger } from '@nestjs/common';
import { ProviderItem as ProviderItemModel } from '@prisma/client';
import { LLMModelConfig, GenericToolset } from '@refly/openapi-schema';
import {
  isAutoModel,
  selectAutoModel,
  AUTO_MODEL_ROUTING_PRIORITY,
  safeParseJSON,
--

  /**
   * Toolsets selected for the skill invocation
   * Used by tool-based routing to check for specific tools
   */
  toolsets?: GenericToolset[];
}

/**
 * Auto model router for selecting the best available model
 * Instantiate with RouterContext and call route() to perform routing

Script executed:

# Check the types or interfaces imported in auto-model-router.service.ts
head -30 apps/api/src/modules/provider/auto-model-router.service.ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

import { Logger } from '@nestjs/common';
import { ProviderItem as ProviderItemModel } from '@prisma/client';
import { LLMModelConfig, GenericToolset } from '@refly/openapi-schema';
import {
  isAutoModel,
  selectAutoModel,
  AUTO_MODEL_ROUTING_PRIORITY,
  safeParseJSON,
  getToolBasedRoutingConfig,
} from '@refly/utils';
import { ProviderItemNotFoundError } from '@refly/errors';

/**
 * Context for Auto model routing
 * Contains all the data needed for routing decisions
 */
export interface RouterContext {
  /**
   * LLM provider items available for the user
   * Pre-fetched by ProviderService.findProviderItemsByCategory(user, 'llm')
   */
  llmItems: ProviderItemModel[];

  /**
   * User identifier for logging purposes
   */
  userId: string;

  /**
   * Scene/mode for the skill invocation (e.g., 'node_agent', 'copilot_agent', 'chat')

Script executed:

# Find where GenericToolset is defined
find apps -name "*.ts" -type f -exec grep -l "export.*GenericToolset|type GenericToolset|interface GenericToolset" {} \;

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0


Script executed:

# Look at how tool.service.ts constructs the UserTool response with keys
rg -B 10 -A 10 "key: tool.toolset\?.key \|\| tool.id" apps/api/src --type ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

apps/api/src/modules/tool/tool.service.ts-    // 3. Get all external OAuth tools from inventory that are not installed
apps/api/src/modules/tool/tool.service.ts-    // external_oauth type tools have requiresAuth=true and authPatterns with type='oauth'
apps/api/src/modules/tool/tool.service.ts-    const allDefinitions = await this.inventoryService.getInventoryDefinitions();
apps/api/src/modules/tool/tool.service.ts-    const unauthorizedTools = allDefinitions.filter(
apps/api/src/modules/tool/tool.service.ts-      (def) => this.shouldExposeToolset(def.key) && def.requiresAuth && !installedKeys.has(def.key),
apps/api/src/modules/tool/tool.service.ts-    );
apps/api/src/modules/tool/tool.service.ts-
apps/api/src/modules/tool/tool.service.ts-    // 4. Build result: authorized tools first, then unauthorized
apps/api/src/modules/tool/tool.service.ts-    const authorizedItems: UserTool[] = populatedTools.map((tool) => ({
apps/api/src/modules/tool/tool.service.ts-      toolsetId: tool.id,
apps/api/src/modules/tool/tool.service.ts:      key: tool.toolset?.key || tool.id,
apps/api/src/modules/tool/tool.service.ts-      name: tool.name,
apps/api/src/modules/tool/tool.service.ts-      description: (tool.toolset?.definition?.descriptionDict?.en as string) || tool.name,
apps/api/src/modules/tool/tool.service.ts-      authorized: true,
apps/api/src/modules/tool/tool.service.ts-      domain: tool.toolset?.definition?.domain,
apps/api/src/modules/tool/tool.service.ts-      toolset: tool,
apps/api/src/modules/tool/tool.service.ts-    }));
apps/api/src/modules/tool/tool.service.ts-
apps/api/src/modules/tool/tool.service.ts-    const unauthorizedItems: UserTool[] = unauthorizedTools.map((def) => ({
apps/api/src/modules/tool/tool.service.ts-      toolsetId: def.key,
apps/api/src/modules/tool/tool.service.ts-      key: def.key,

Script executed:

# Find GenericToolset type definition in packages
find packages -name "*.ts" -type f -exec grep -l "export.*GenericToolset|type GenericToolset" {} \;

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0


Script executed:

# Look at how the formatter handles different toolset types and what the context should include
rg -B 3 -A 8 "genericToolset.type === 'regular'" apps/api/src --type ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

apps/api/src/modules/pilot/prompt/formatter.ts-  if ('type' in toolset) {
apps/api/src/modules/pilot/prompt/formatter.ts-    const genericToolset = toolset as GenericToolset;
apps/api/src/modules/pilot/prompt/formatter.ts-    const key =
apps/api/src/modules/pilot/prompt/formatter.ts:      genericToolset.type === 'regular'
apps/api/src/modules/pilot/prompt/formatter.ts-        ? (genericToolset.toolset?.key ?? 'unknown-key')
apps/api/src/modules/pilot/prompt/formatter.ts-        : (genericToolset.name ?? 'unknown-name');
apps/api/src/modules/pilot/prompt/formatter.ts-
apps/api/src/modules/pilot/prompt/formatter.ts-    const description =
apps/api/src/modules/pilot/prompt/formatter.ts-      genericToolset.toolset?.definition?.descriptionDict?.en ?? 'No description available';
apps/api/src/modules/pilot/prompt/formatter.ts-
apps/api/src/modules/pilot/prompt/formatter.ts-    const tools = genericToolset.toolset?.definition?.tools ?? [];
apps/api/src/modules/pilot/prompt/formatter.ts-    const toolList = tools

Script executed:

# Check what types of toolsets can appear in context
rg "type === 'regular'|type !== 'regular'|ToolsetType" apps/api/src/modules --type ts | head -30

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

apps/api/src/modules/tool/constant/constant.ts:import type { GenericToolsetType, ToolsetAuthType } from '@refly/openapi-schema';
apps/api/src/modules/tool/constant/constant.ts:export const ToolsetType: Record<string, GenericToolsetType> = {
apps/api/src/modules/tool/constant/constant.ts:export type ToolsetTypeValue = GenericToolsetType;
apps/api/src/modules/tool/tool.dto.ts:import { ToolsetType } from './constant';
apps/api/src/modules/tool/tool.dto.ts:  type: ToolsetType.MCP,
apps/api/src/modules/tool/tool.dto.ts:    type: ToolsetType.EXTERNAL_OAUTH,
apps/api/src/modules/tool/tool.service.ts:import { AuthType, ToolsetType } from './constant';
apps/api/src/modules/tool/tool.service.ts:        type: ToolsetType.REGULAR,
apps/api/src/modules/tool/tool.service.ts:      if (type === ToolsetType.MCP) {
apps/api/src/modules/tool/tool.service.ts:      if (toolset.type === ToolsetType.REGULAR) {
apps/api/src/modules/tool/tool.service.ts:      } else if (toolset.type === ToolsetType.MCP) {
apps/api/src/modules/tool/tool.service.ts:      } else if (toolset.type === ToolsetType.EXTERNAL_OAUTH) {
apps/api/src/modules/tool/tool.service.ts:      .filter((t) => t.type === ToolsetType.REGULAR && t.builtin)
apps/api/src/modules/tool/tool.service.ts:    if (toolsets.find((t) => t.type === ToolsetType.REGULAR && t.id === 'copilot')) {
apps/api/src/modules/tool/tool.service.ts:    const regularToolsets = toolsets.filter((t) => t.type === ToolsetType.REGULAR && !t.builtin);
apps/api/src/modules/tool/tool.service.ts:    const mcpServers = toolsets.filter((t) => t.type === ToolsetType.MCP);
apps/api/src/modules/tool/tool.service.ts:              type: ToolsetType.REGULAR,
apps/api/src/modules/tool/inventory/inventory.service.ts:  GenericToolsetType,
apps/api/src/modules/tool/inventory/inventory.service.ts:        type: item.type as GenericToolsetType,
apps/api/src/modules/pilot/prompt/formatter.ts:      genericToolset.type === 'regular'

Script executed:

# Verify by looking at skill.service where FIXED_BUILTIN_TOOLSETS is defined
rg -B 5 -A 15 "FIXED_BUILTIN_TOOLSETS" apps/api/src --type ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

apps/api/src/modules/skill/skill.service.ts-/**
apps/api/src/modules/skill/skill.service.ts- * Fixed builtin toolsets that are always available for node_agent mode.
apps/api/src/modules/skill/skill.service.ts- * These toolsets will be automatically appended to user-selected toolsets.
apps/api/src/modules/skill/skill.service.ts- * Note: IDs must match BuiltinToolsetDefinition.tools[].name for instantiateBuiltinToolsets to work.
apps/api/src/modules/skill/skill.service.ts- */
apps/api/src/modules/skill/skill.service.ts:const FIXED_BUILTIN_TOOLSETS: GenericToolset[] = [
apps/api/src/modules/skill/skill.service.ts-  { type: 'regular', id: 'execute_code', name: 'execute_code', builtin: true },
apps/api/src/modules/skill/skill.service.ts-  { type: 'regular', id: 'read_file', name: 'read_file', builtin: true },
apps/api/src/modules/skill/skill.service.ts-  { type: 'regular', id: 'get_time', name: 'get_time', builtin: true },
apps/api/src/modules/skill/skill.service.ts-];
apps/api/src/modules/skill/skill.service.ts-
apps/api/src/modules/skill/skill.service.ts-function validateSkillTriggerCreateParam(param: SkillTriggerCreateParam) {
apps/api/src/modules/skill/skill.service.ts-  if (param.triggerType === 'simpleEvent') {
apps/api/src/modules/skill/skill.service.ts-    if (!param.simpleEventName) {
apps/api/src/modules/skill/skill.service.ts-      throw new ParamsError('invalid event trigger config');
apps/api/src/modules/skill/skill.service.ts-    }
apps/api/src/modules/skill/skill.service.ts-  } else if (param.triggerType === 'timer') {
apps/api/src/modules/skill/skill.service.ts-    if (!param.timerConfig) {
apps/api/src/modules/skill/skill.service.ts-      throw new ParamsError('invalid timer trigger config');
apps/api/src/modules/skill/skill.service.ts-    }
apps/api/src/modules/skill/skill.service.ts-  }
--
apps/api/src/modules/skill/skill.service.ts-   */
apps/api/src/modules/skill/skill.service.ts-  private appendFixedToolset(toolsets?: GenericToolset[]): GenericToolset[] {
apps/api/src/modules/skill/skill.service.ts-    const userToolsets = toolsets ?? [];
apps/api/src/modules/skill/skill.service.ts-    const existingIds = new Set(userToolsets.map((t) => t.id));
apps/api/src/modules/skill/skill.service.ts-
apps/api/src/modules/skill/skill.service.ts:    const toolsetsToAppend = FIXED_BUILTIN_TOOLSETS.filter((t) => !existingIds.has(t.id));
apps/api/src/modules/skill/skill.service.ts-
apps/api/src/modules/skill/skill.service.ts-    return [...userToolsets, ...toolsetsToAppend];
apps/api/src/modules/skill/skill.service.ts-  }
apps/api/src/modules/skill/skill.service.ts-
apps/api/src/modules/skill/skill.service.ts-  private async prepareInvokeSkillJobData(
apps/api/src/modules/skill/skill.service.ts-    user: User,
apps/api/src/modules/skill/skill.service.ts-    param: InvokeSkillRequest,
apps/api/src/modules/skill/skill.service.ts-  ): Promise<{
apps/api/src/modules/skill/skill.service.ts-    data: InvokeSkillJobData;
apps/api/src/modules/skill/skill.service.ts-    existingResult: ActionResultModel;
apps/api/src/modules/skill/skill.service.ts-    providerItem: ProviderItemModel;
apps/api/src/modules/skill/skill.service.ts-  }> {
apps/api/src/modules/skill/skill.service.ts-    const { uid } = user;
apps/api/src/modules/skill/skill.service.ts-    const resultId = param.resultId || genActionResultID();
apps/api/src/modules/skill/skill.service.ts-

Script executed:

# Check getToolBasedRoutingConfig to understand what targetTools contains
rg -A 20 "export.*getToolBasedRoutingConfig|function getToolBasedRoutingConfig" packages --type ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

packages/utils/src/auto-model.ts:export const getToolBasedRoutingConfig = (): ToolBasedRoutingConfig => {
packages/utils/src/auto-model.ts-  const enabled = process.env.AUTO_MODEL_ROUTING_TOOL_BASED_ENABLED === 'true';
packages/utils/src/auto-model.ts-
packages/utils/src/auto-model.ts-  const targetToolsStr = process.env.AUTO_MODEL_ROUTING_TOOL_BASED_TARGET_TOOLS;
packages/utils/src/auto-model.ts-  const targetTools = targetToolsStr
packages/utils/src/auto-model.ts-    ? targetToolsStr
packages/utils/src/auto-model.ts-        .split(',')
packages/utils/src/auto-model.ts-        .map((tool) => tool.trim())
packages/utils/src/auto-model.ts-        .filter((tool) => tool.length > 0)
packages/utils/src/auto-model.ts-    : [];
packages/utils/src/auto-model.ts-
packages/utils/src/auto-model.ts-  const matchedModelId = process.env.AUTO_MODEL_ROUTING_TOOL_BASED_MATCHED_MODEL_ID?.trim() || null;
packages/utils/src/auto-model.ts-  const unmatchedModelId =
packages/utils/src/auto-model.ts-    process.env.AUTO_MODEL_ROUTING_TOOL_BASED_UNMATCHED_MODEL_ID?.trim() || null;
packages/utils/src/auto-model.ts-
packages/utils/src/auto-model.ts-  return {
packages/utils/src/auto-model.ts-    enabled,
packages/utils/src/auto-model.ts-    targetTools,
packages/utils/src/auto-model.ts-    matchedModelId,
packages/utils/src/auto-model.ts-    unmatchedModelId,
packages/utils/src/auto-model.ts-  };

Script executed:

# Look at the GenericToolset structure definition
rg "export.*GenericToolset\|export type GenericToolset" packages --type ts -A 15 | head -60

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0


Script executed:

# Check if builtin toolsets can appear in the context being passed to tryToolBasedRouting
rg -B 10 "tryToolBasedRouting|new AutoModelRouter" apps/api/src/modules/provider --type ts | head -40

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

apps/api/src/modules/provider/auto-model-router.service.ts-      if (config.capabilities?.reasoning === true) {
apps/api/src/modules/provider/auto-model-router.service.ts-        continue;
apps/api/src/modules/provider/auto-model-router.service.ts-      }
apps/api/src/modules/provider/auto-model-router.service.ts-
apps/api/src/modules/provider/auto-model-router.service.ts-      if (config.modelId) {
apps/api/src/modules/provider/auto-model-router.service.ts-        modelMap.set(config.modelId, item);
apps/api/src/modules/provider/auto-model-router.service.ts-      }
apps/api/src/modules/provider/auto-model-router.service.ts-    }
apps/api/src/modules/provider/auto-model-router.service.ts-
apps/api/src/modules/provider/auto-model-router.service.ts-    // Priority 0: Tool-based routing (if enabled)
apps/api/src/modules/provider/auto-model-router.service.ts:    const toolBasedModel = this.tryToolBasedRouting(modelMap);
--
apps/api/src/modules/provider/auto-model-router.service.ts-    throw new ProviderItemNotFoundError('Auto model routing failed: no model available');
apps/api/src/modules/provider/auto-model-router.service.ts-  }
apps/api/src/modules/provider/auto-model-router.service.ts-
apps/api/src/modules/provider/auto-model-router.service.ts-  /**
apps/api/src/modules/provider/auto-model-router.service.ts-   * Try tool-based routing logic
apps/api/src/modules/provider/auto-model-router.service.ts-   * This implements the temporary tool-based routing strategy controlled by environment variables
apps/api/src/modules/provider/auto-model-router.service.ts-   *
apps/api/src/modules/provider/auto-model-router.service.ts-   * @param modelMap Map of available models (modelId -> ProviderItem)
apps/api/src/modules/provider/auto-model-router.service.ts-   * @returns The selected provider item, or null if tool-based routing should not be applied
apps/api/src/modules/provider/auto-model-router.service.ts-   */
apps/api/src/modules/provider/auto-model-router.service.ts:  private tryToolBasedRouting(modelMap: Map<string, ProviderItemModel>): ProviderItemModel | null {

Script executed:

# See where RouterContext is created with toolsets
rg -B 15 "RouterContext.*toolsets|toolsets.*GenericToolset" apps/api/src --type ts | head -60

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

apps/api/src/modules/tool/tool.dto.ts-    authType: toolset.authType as ToolsetAuthType,
apps/api/src/modules/tool/tool.dto.ts-    config: safeParseJSON(toolset.config),
apps/api/src/modules/tool/tool.dto.ts-    definition: inventoryItem?.definition,
apps/api/src/modules/tool/tool.dto.ts-    createdAt: toolset.createdAt.toJSON(),
apps/api/src/modules/tool/tool.dto.ts-    updatedAt: toolset.updatedAt.toJSON(),
apps/api/src/modules/tool/tool.dto.ts-  };
apps/api/src/modules/tool/tool.dto.ts-};
apps/api/src/modules/tool/tool.dto.ts-
apps/api/src/modules/tool/tool.dto.ts-/**
apps/api/src/modules/tool/tool.dto.ts- * Populate toolsets with definitions from inventory (synchronous version with inventoryMap)
apps/api/src/modules/tool/tool.dto.ts- * @param toolsets - Array of generic toolsets to populate
apps/api/src/modules/tool/tool.dto.ts- * @param inventoryMap - Inventory map from ToolInventoryService
apps/api/src/modules/tool/tool.dto.ts- * @returns Populated toolsets with definitions
apps/api/src/modules/tool/tool.dto.ts- */
apps/api/src/modules/tool/tool.dto.ts-export const populateToolsets = (
apps/api/src/modules/tool/tool.dto.ts:  toolsets: GenericToolset[],
--
apps/api/src/modules/tool/composio/composio.service.ts-          },
apps/api/src/modules/tool/composio/composio.service.ts-        });
apps/api/src/modules/tool/composio/composio.service.ts-      }
apps/api/src/modules/tool/composio/composio.service.ts-    });
apps/api/src/modules/tool/composio/composio.service.ts-  }
apps/api/src/modules/tool/composio/composio.service.ts-
apps/api/src/modules/tool/composio/composio.service.ts-  /**
apps/api/src/modules/tool/composio/composio.service.ts-   * Instantiate Composio toolsets into structured tools
apps/api/src/modules/tool/composio/composio.service.ts-   * Converts Composio tools into LangChain DynamicStructuredTool instances
apps/api/src/modules/tool/composio/composio.service.ts-   * @param user - The user requesting the tools
apps/api/src/modules/tool/composio/composio.service.ts-   * @param toolsets - Array of toolsets to instantiate
apps/api/src/modules/tool/composio/composio.service.ts-   * @param authType - Authentication type: 'oauth' (user-specific) or 'apikey' (global shared)
apps/api/src/modules/tool/composio/composio.service.ts-   */
apps/api/src/modules/tool/composio/composio.service.ts-  async instantiateToolsets(
apps/api/src/modules/tool/composio/composio.service.ts-    user: User,
apps/api/src/modules/tool/composio/composio.service.ts:    toolsets: GenericToolset[],
--
apps/api/src/modules/tool/tool.service.ts-
apps/api/src/modules/tool/tool.service.ts-  async listTools(user: User, param?: ListToolsData['query']): Promise<GenericToolset[]> {
apps/api/src/modules/tool/tool.service.ts-    const builtinTools = this.listBuiltinTools();
apps/api/src/modules/tool/tool.service.ts-    const [regularTools, oauthTools, mcpTools] = await Promise.all([
apps/api/src/modules/tool/tool.service.ts-      this.listRegularTools(user, param), // Includes both regular
apps/api/src/modules/tool/tool.service.ts-      this.listOAuthTools(user, param), // OAuth tools from Composio connections
apps/api/src/modules/tool/tool.service.ts-      this.listMcpTools(user, param), // MCP server tools
apps/api/src/modules/tool/tool.service.ts-    ]);
apps/api/src/modules/tool/tool.service.ts-    return [...builtinTools, ...regularTools, ...oauthTools, ...mcpTools];
apps/api/src/modules/tool/tool.service.ts-  }
apps/api/src/modules/tool/tool.service.ts-
apps/api/src/modules/tool/tool.service.ts-  /**
apps/api/src/modules/tool/tool.service.ts-   * Populate toolsets with definitions from inventory (for canvas and other use cases)
apps/api/src/modules/tool/tool.service.ts-   * This is a simpler version that only uses inventory data, no user-specific data
apps/api/src/modules/tool/tool.service.ts-   */
apps/api/src/modules/tool/tool.service.ts:  async populateToolsetsWithDefinition(toolsets: GenericToolset[]): Promise<GenericToolset[]> {
--
apps/api/src/modules/tool/tool.service.ts-      },
apps/api/src/modules/tool/tool.service.ts-    });
apps/api/src/modules/tool/tool.service.ts-
apps/api/src/modules/tool/tool.service.ts-    if (!toolset) {
apps/api/src/modules/tool/tool.service.ts-      throw new ToolsetNotFoundError(`Toolset ${toolsetId} not found`);
apps/api/src/modules/tool/tool.service.ts-    }
apps/api/src/modules/tool/tool.service.ts-
apps/api/src/modules/tool/tool.service.ts-    await this.prisma.toolset.update({
apps/api/src/modules/tool/tool.service.ts-      where: { pk: toolset.pk },

Script executed:

# Check what targetTools would contain - are they toolset IDs or keys?
rg "AUTO_MODEL_ROUTING_TOOL_BASED_TARGET_TOOLS" --type md,txt --type-add "md:include:*.md" --type-add "txt:include:*.txt,.env*"

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 2

stderr:

invalid definition (format is type:glob, e.g., html:*.html)

Script executed:

# Look for environment variable documentation
find . -name "*.md" -o -name ".env*" -o -name "*.txt" | xargs grep -l "AUTO_MODEL_ROUTING" 2>/dev/null | head -10

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0


Script executed:

# Check if builtin toolsets actually make it into the context.toolsets array
rg -B 5 -A 5 "FIXED_BUILTIN_TOOLSETS" apps/api/src/modules/skill --type ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

apps/api/src/modules/skill/skill.service.ts-/**
apps/api/src/modules/skill/skill.service.ts- * Fixed builtin toolsets that are always available for node_agent mode.
apps/api/src/modules/skill/skill.service.ts- * These toolsets will be automatically appended to user-selected toolsets.
apps/api/src/modules/skill/skill.service.ts- * Note: IDs must match BuiltinToolsetDefinition.tools[].name for instantiateBuiltinToolsets to work.
apps/api/src/modules/skill/skill.service.ts- */
apps/api/src/modules/skill/skill.service.ts:const FIXED_BUILTIN_TOOLSETS: GenericToolset[] = [
apps/api/src/modules/skill/skill.service.ts-  { type: 'regular', id: 'execute_code', name: 'execute_code', builtin: true },
apps/api/src/modules/skill/skill.service.ts-  { type: 'regular', id: 'read_file', name: 'read_file', builtin: true },
apps/api/src/modules/skill/skill.service.ts-  { type: 'regular', id: 'get_time', name: 'get_time', builtin: true },
apps/api/src/modules/skill/skill.service.ts-];
apps/api/src/modules/skill/skill.service.ts-
--
apps/api/src/modules/skill/skill.service.ts-   */
apps/api/src/modules/skill/skill.service.ts-  private appendFixedToolset(toolsets?: GenericToolset[]): GenericToolset[] {
apps/api/src/modules/skill/skill.service.ts-    const userToolsets = toolsets ?? [];
apps/api/src/modules/skill/skill.service.ts-    const existingIds = new Set(userToolsets.map((t) => t.id));
apps/api/src/modules/skill/skill.service.ts-
apps/api/src/modules/skill/skill.service.ts:    const toolsetsToAppend = FIXED_BUILTIN_TOOLSETS.filter((t) => !existingIds.has(t.id));
apps/api/src/modules/skill/skill.service.ts-
apps/api/src/modules/skill/skill.service.ts-    return [...userToolsets, ...toolsetsToAppend];
apps/api/src/modules/skill/skill.service.ts-  }
apps/api/src/modules/skill/skill.service.ts-
apps/api/src/modules/skill/skill.service.ts-  private async prepareInvokeSkillJobData(

Script executed:

# Verify the complete GenericToolset type to understand all possible properties
rg "packages/openapi-schema" apps/api/src/modules/provider/auto-model-router.service.ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 1


Script executed:

# Find the actual GenericToolset type definition
find packages/openapi-schema -name "*.ts" -exec grep -l "GenericToolset" {} \;

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

packages/openapi-schema/src/schemas.gen.ts
packages/openapi-schema/src/types.gen.ts

Script executed:

# Get the GenericToolset type definition from types.gen.ts
rg -A 30 "export.*GenericToolset|type GenericToolset" packages/openapi-schema/src/types.gen.ts | head -80

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

export type GenericToolsetType = 'regular' | 'mcp' | 'external_oauth';

export type GenericToolset = {
  /**
   * Toolset type
   */
  type: GenericToolsetType;
  /**
   * Toolset ID (toolsetId for regular toolset, name for MCP toolset)
   */
  id: string;
  /**
   * Toolset name
   */
  name: string;
  /**
   * Whether the toolset is builtin
   */
  builtin?: boolean;
  /**
   * Whether the toolset is uninstalled
   */
  uninstalled?: boolean;
  /**
   * Toolset detail
   */
  toolset?: ToolsetInstance;
  /**
   * MCP server
   */
  mcpServer?: McpServerDTO;
  /**
   * Selected tools (used for skill invocation)

Script executed:

# Look at how skill.service uses the toolsets in context
rg -B 10 -A 10 "context.*toolsets|toolsets.*context" apps/api/src/modules/skill --type ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 1


Script executed:

# Check where RouteContext is instantiated
rg -B 20 "new AutoModelRouter|AutoModelRouter\(" apps/api/src --type ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

apps/api/src/modules/skill/skill.service.ts-      param.modelName ??= existingResult.modelName;
apps/api/src/modules/skill/skill.service.ts-      param.modelItemId ??= existingResult.providerItemId;
apps/api/src/modules/skill/skill.service.ts-      param.context ??= safeParseJSON(existingResult.context);
apps/api/src/modules/skill/skill.service.ts-      param.resultHistory ??= safeParseJSON(existingResult.history);
apps/api/src/modules/skill/skill.service.ts-      param.tplConfig ??= safeParseJSON(existingResult.tplConfig);
apps/api/src/modules/skill/skill.service.ts-      param.runtimeConfig ??= safeParseJSON(existingResult.runtimeConfig);
apps/api/src/modules/skill/skill.service.ts-      param.projectId ??= existingResult.projectId;
apps/api/src/modules/skill/skill.service.ts-    }
apps/api/src/modules/skill/skill.service.ts-
apps/api/src/modules/skill/skill.service.ts-    param.input ||= { query: '' };
apps/api/src/modules/skill/skill.service.ts-    param.skillName ||= 'commonQnA';
apps/api/src/modules/skill/skill.service.ts-
apps/api/src/modules/skill/skill.service.ts-    // Auto model routing
apps/api/src/modules/skill/skill.service.ts-    const llmItems = await this.providerService.findProviderItemsByCategory(user, 'llm');
apps/api/src/modules/skill/skill.service.ts-    const routerContext = {
apps/api/src/modules/skill/skill.service.ts-      llmItems,
apps/api/src/modules/skill/skill.service.ts-      userId: user.uid,
apps/api/src/modules/skill/skill.service.ts-      scene: param.mode,
apps/api/src/modules/skill/skill.service.ts-      toolsets: param.toolsets,
apps/api/src/modules/skill/skill.service.ts-    };
apps/api/src/modules/skill/skill.service.ts:    const autoModelRouter = new AutoModelRouter(routerContext);

Script executed:

# Verify the complete GenericToolset type to see all properties
rg -A 50 "export type GenericToolset = " packages/openapi-schema/src/types.gen.ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

export type GenericToolset = {
  /**
   * Toolset type
   */
  type: GenericToolsetType;
  /**
   * Toolset ID (toolsetId for regular toolset, name for MCP toolset)
   */
  id: string;
  /**
   * Toolset name
   */
  name: string;
  /**
   * Whether the toolset is builtin
   */
  builtin?: boolean;
  /**
   * Whether the toolset is uninstalled
   */
  uninstalled?: boolean;
  /**
   * Toolset detail
   */
  toolset?: ToolsetInstance;
  /**
   * MCP server
   */
  mcpServer?: McpServerDTO;
  /**
   * Selected tools (used for skill invocation)
   */
  selectedTools?: Array<string>;
};

export type ListToolsResponse = BaseResponse & {
  data?: Array<GenericToolset>;
};

export type ListUserToolsResponse = BaseResponse & {
  data?: Array<UserTool>;
};

export type UserTool = {
  /**
   * Toolset ID (toolsetId for installed, key for uninstalled)
   */
  toolsetId?: string;
  /**
   * Toolset key
   */

Script executed:

# Check ToolsetInstance to understand what key is
rg -B 5 -A 15 "type ToolsetInstance|interface ToolsetInstance" packages/openapi-schema/src/types.gen.ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

   * Toolset config items
   */
  configItems?: Array<DynamicConfigItem>;
};

export type ToolsetInstance = {
  /**
   * Toolset ID
   */
  toolsetId: string;
  /**
   * Toolset name
   */
  name: string;
  /**
   * Toolset key
   */
  key: string;
  /**
   * Whether the toolset is global
   */

Script executed:

# Look for how targetTools are configured in examples or tests
rg "AUTO_MODEL_ROUTING_TOOL_BASED_TARGET_TOOLS|targetTools" --type ts --type json -B 2 -A 2

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

packages/utils/src/auto-model.ts-export interface ToolBasedRoutingConfig {
packages/utils/src/auto-model.ts-  enabled: boolean;
packages/utils/src/auto-model.ts:  targetTools: string[];
packages/utils/src/auto-model.ts-  matchedModelId: string | null;
packages/utils/src/auto-model.ts-  unmatchedModelId: string | null;
--
packages/utils/src/auto-model.ts-  const enabled = process.env.AUTO_MODEL_ROUTING_TOOL_BASED_ENABLED === 'true';
packages/utils/src/auto-model.ts-
packages/utils/src/auto-model.ts:  const targetToolsStr = process.env.AUTO_MODEL_ROUTING_TOOL_BASED_TARGET_TOOLS;
packages/utils/src/auto-model.ts:  const targetTools = targetToolsStr
packages/utils/src/auto-model.ts:    ? targetToolsStr
packages/utils/src/auto-model.ts-        .split(',')
packages/utils/src/auto-model.ts-        .map((tool) => tool.trim())
--
packages/utils/src/auto-model.ts-  return {
packages/utils/src/auto-model.ts-    enabled,
packages/utils/src/auto-model.ts:    targetTools,
packages/utils/src/auto-model.ts-    matchedModelId,
packages/utils/src/auto-model.ts-    unmatchedModelId,
--
apps/api/src/modules/provider/auto-model-router.service.ts-    );
apps/api/src/modules/provider/auto-model-router.service.ts-
apps/api/src/modules/provider/auto-model-router.service.ts:    const hasTargetTool = config.targetTools.some((targetTool) => toolKeysSet.has(targetTool));
apps/api/src/modules/provider/auto-model-router.service.ts-
apps/api/src/modules/provider/auto-model-router.service.ts-    const targetModelId = hasTargetTool ? config.matchedModelId : config.unmatchedModelId;

Script executed:

# Verify how the formatter extracts keys for logging/comparison - compare with auto-model-router
rg -B 3 -A 15 "genericToolset.type === 'regular'" apps/api/src/modules/pilot/prompt/formatter.ts

Repository: refly-ai/refly


Repository: refly-ai/refly
Exit code: 0

stdout:

  if ('type' in toolset) {
    const genericToolset = toolset as GenericToolset;
    const key =
      genericToolset.type === 'regular'
        ? (genericToolset.toolset?.key ?? 'unknown-key')
        : (genericToolset.name ?? 'unknown-name');

    const description =
      genericToolset.toolset?.definition?.descriptionDict?.en ?? 'No description available';

    const tools = genericToolset.toolset?.definition?.tools ?? [];
    const toolList = tools
      .map((tool) => {
        const toolDescription =
          tool.descriptionDict?.en ?? tool.descriptionDict?.['zh-CN'] ?? 'No description available';
        return `  - ${tool.name}: ${toolDescription}`;
      })
      .join('\n');


Fix tool key extraction for builtin toolsets.

The tool key extraction at lines 165-167 silently drops builtin toolsets. When t.toolset is undefined (which occurs for builtin toolsets like execute_code), the value is filtered out, preventing them from matching configured targetTools.

Apply this diff:

     const toolKeysSet = new Set(
-      this.context.toolsets?.map((t) => t.toolset?.key).filter((key): key is string => !!key) ?? [],
+      this.context.toolsets?.map((t) => t.toolset?.key ?? t.name ?? t.id).filter((key): key is string => !!key) ?? [],
     );
🤖 Prompt for AI Agents
In apps/api/src/modules/provider/auto-model-router.service.ts around lines
155-190, the current tool key extraction silently drops builtin toolsets because
it only reads t.toolset?.key; change the mapping to fall back to a top-level key
when toolset is undefined (e.g. use t.toolset?.key ?? t.key), keep the filter to
remove falsy values, and update the type guard accordingly so builtin tool keys
like "execute_code" are included in toolKeysSet for matching.

- Add tool-based routing logic to AutoModelRouter with configurable target tools and model selection
- Extend RouterContext with scene and toolsets fields for routing decision
- Add getToolBasedRoutingConfig utility to read routing config from environment variables
- Integrate tool-based routing in SkillService and WorkflowService by passing scene and toolsets context
@nettee nettee force-pushed the feat/auto-model-routing-tool-based branch from 7ad9fbc to 1c13786 Compare December 17, 2025 08:56
@mrcfps mrcfps merged commit 669edc4 into main Dec 17, 2025
2 checks passed
@mrcfps mrcfps deleted the feat/auto-model-routing-tool-based branch December 17, 2025 09:20
mrcfps added a commit that referenced this pull request Dec 18, 2025
* fix: duplicate manual upload files when executing workflow app (#1829)

* feat: Introduce `UseShareDataProvider` to control data source for shared content and refactor file ID handling for shared canvas data.

* refactor: use `useRef` for stable references in `SkillResponse` and `replaceAll` for safer file ID replacement.

* feat: Duplicate manual canvas files and update their references when duplicating a workflow app.

* feat: Implement credit pack, voucher generation and sharing for template publishing #1771 (#1830)

* Feat/credit pack (#1748)

* feat: implement credit pack feature with enhanced subscription and onboarding

- Add credit pack purchase functionality
- Implement onboarding success modal with animation
- Add form-based user onboarding system
- Enhance subscription management with new pricing plans
- Improve credit handling and error messaging
- Add telemetry logging for user interactions
- Update UI components and translations

* refactor: remove unused schemas and enhance workflow execution polling

- Deleted the schemas.ts file as it was no longer needed.
- Updated use-workflow-execution-polling hook to include useSubscriptionStoreShallow for improved state management.

* refactor: improve subscription component styling and logic

- Refactored PriceOption and FeatureItem components for better performance and maintainability.
- Updated Tailwind CSS classes for consistent styling across components.
- Implemented optional chaining and nullish coalescing for safer property access.
- Enhanced feature description display logic based on plan type.
- Adjusted layout and spacing for improved user experience in subscription-related components.

* feat: add currentPlan and source fields to checkout session models an… (#1753)

feat: add currentPlan and source fields to checkout session models and requests

- Introduced currentPlan and source fields in the CheckoutSession model for better tracking of subscription details.
- Updated CreateCheckoutSessionRequest and CreateCreditPackCheckoutSessionRequest types to include currentPlan and source.
- Enhanced subscription service and webhook logic to utilize new fields for improved telemetry logging and user experience.
- Refactored related components to pass currentPlan and source during checkout session creation.

* Feat/creator community (#1778)

* feat: add promotion item component and community SVG asset

- Introduced a new PromotionItem component to display promotional offers in the sidebar, utilizing Tailwind CSS for styling.
- Added community.svg asset for visual representation in the PromotionItem.
- Updated translations for promotion-related text in both English and Chinese.
- Ensured proper handling of collapsed state and click events for opening promotion URLs.

* fix: update PromotionItem component styling and enhance button accessibility

- Adjusted the width and border radius of the PromotionItem component for improved aesthetics.
- Enhanced the CTA button styling to support dark mode and improve hover effects.
- Ensured compliance with Tailwind CSS for consistent styling across the component.

* fix: update PromotionItem component styling and translations

- Adjusted padding and text styles in the PromotionItem component for improved visual consistency.
- Updated translation keys for promotion-related text to enhance clarity and user experience.
- Ensured compliance with Tailwind CSS for consistent styling across the component.

* fix: refine PromotionItem component styling and update translations

- Adjusted padding and text styles in the PromotionItem component for improved layout and visual appeal.
- Updated the promotion description in translations for better clarity and user engagement.
- Ensured compliance with Tailwind CSS for consistent styling across the component.

* feat(subscription): enhance subscription handling and user type management

- Updated subscription DTO to include 'lookupKey' in the mapping.
- Integrated user type management in the PromotionItem component, utilizing the subscription store for better user context.
- Enhanced user settings hook to set subscription plan type and user type based on retrieved settings.
- Expanded subscription plan type to include 'pro' across various schemas and types for consistency.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* feat: Implement voucher generation and sharing for template publishing (#1771)

* feat: implement voucher system with sharing, application, and rewards

* feat: Implement voucher system with API for triggering generation, pending claim handling, and new UI components.

* feat: implement Stripe promotion codes for vouchers, add `StripeCoupon` model, and refine discount percentage mapping.

* fix: use semantic dialog element instead of div with role

Replace div with role="dialog" with native <dialog> element to satisfy
biome a11y/useSemanticElements lint rule.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* feat: enhance voucher popup UI with new confetti animation, Ant Design buttons, and improved invitation verification types

* feat: enhance voucher claiming and sharing by adding inviter name display, refining UI logic, and improving share link copy functionality.

* chore: Remove voucher invite page and its corresponding route.

* feat: Implement voucher invite flow tracking for signup events and adjust associated redirects and share URLs.

* fix: Set daily voucher popup limit to 3 and change agent file write type to text/plain.

* docs: Remove deprecated voucher implementation and design documents.

* refactor: Streamline voucher invitation handling by removing `shareUrl` and `qrCodeUrl` from API and standardizing event logging.

* feat: Add user_type to various telemetry events for enhanced tracking.

* feat: Enhance voucher invitation flow by showing claimed but unused vouchers, preserving invite codes during login, and removing popup limits.

---------

Co-authored-by: Claude Opus 4.5 <[email protected]>

* feat: Implement voucher email notifications with new templates and integrate with the notification module.

* Remove invite code handling, `useSearchParams`, and associated redirects from the HomeRedirect component.

* feat: Update voucher invite links to point to `/workspace` and enhance the claim hook to prioritize URL parameters.

* feat: Redesign the voucher email template with enhanced styling and a new table-based layout.

* refactor: remove SubscriptionService dependency from DriveService

---------

Co-authored-by: Siri-Ray <[email protected]>
Co-authored-by: Claude Opus 4.5 <[email protected]>

* feat: enhance logout functionality with user state reset (#1831)

- Added `resetUserState` parameter to the `logout` function to reset user state in the store upon logout.
- Integrated `useUserStoreShallow` to access the `resetState` method within the `useLogout` hook.
- Ensured compliance with coding standards, including optional chaining for safe property access.

* feat: Add auto model routing tracking for token usage (#1834)

- Add originalModelId and modelRoutedData fields to TokenUsage schema to track model routing history
- Update skill invoker service to extract and store routing metadata when models are routed
- Add routing data support in subscription service for token usage recording
- Update OpenAPI schema with model routing metadata structure including routing strategy and timestamps
- Add database index on originalModelId for efficient querying of auto-routed model usage

* [Security] Fix CRITICAL vulnerability: V-001 (#1832)

fix: resolve critical vulnerability V-001

Automatically generated security fix

Co-authored-by: orbisai0security <[email protected]>

* feat: add workflow template generation status tracking (#1793)

* feat: add template generation status tracking and UI components

- Enhanced the WorkflowApp model to include fields for template generation status and error messages.
- Implemented a new service method to retrieve the template generation status for a workflow app.
- Added a polling hook to manage template generation status updates in the UI.
- Created a TemplateStatusBadge component to visually represent the generation status.
- Updated the WorkflowAPPForm to integrate the new template status functionality and display the badge conditionally.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* feat: enhance TemplateStatusBadge with ripple animation and improved styling

- Added a ripple animation effect for the completed status using a new RippleWrapper component.
- Implemented useEffect to manage the addition and cleanup of the ripple animation style.
- Updated Badge component styling with Tailwind CSS for better visual consistency.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* feat: add template generation messages to i18n translations

- Introduced new translation keys for template generation status in both English and Chinese.
- Updated the TemplateStatusBadge component to utilize the new translations for improved user feedback.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* feat: implement auto-switch functionality for template editor

- Added logic to automatically switch to the editor if the page refreshes with an existing completed template, enhancing user experience.
- Introduced refs to track initial status and check for auto-switch conditions, ensuring the switch only occurs under specific criteria.
- Updated useEffect hooks to manage state and side effects related to template content and user interactions.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* feat: validate appId in template generation status request

- Added a check to ensure appId is provided and not empty, throwing a BadRequestException if validation fails.
- Updated the handling of template generation status to account for null values from old records, defaulting to 'idle' when necessary.
- Enhanced logging for status mismatches when content exists but the status indicates otherwise.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* feat: improve error logging and status updates in WorkflowAppTemplateProcessor

- Enhanced logging messages to include job IDs and app IDs for better traceability during template generation failures.
- Added checks to ensure appId is available before updating generation status, preventing unnecessary updates.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* feat: enhance WorkflowAPPForm with dynamic polling and status tracking

- Introduced a new state to control polling based on user interactions and template status, improving responsiveness during regeneration scenarios.
- Added logic to track previous status for better detection of status changes, ensuring accurate updates during template generation.
- Updated useEffect hooks to manage polling state and status changes effectively, preventing unnecessary re-renders and ensuring compliance with coding standards.
- Enhanced TemplateStatusBadge component with proper styling using Tailwind CSS.
- Ensured compliance with optional chaining and nullish coalescing for safer property access.

* feat: add template generation status queries and hooks

- Introduced new queries and hooks for retrieving and managing template generation status, enhancing the workflow app's capabilities.
- Implemented ensureQueryData and prefetchQuery functions for efficient data handling.
- Updated the OpenAPI schema to include the new endpoint for fetching template generation status.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.
- Enhanced the useTemplateGenerationStatus hook to support polling and manual control for better user experience.

* feat: update template generation messages in i18n translations

- Revised translation strings for template generation status in both English and Chinese to provide clearer user feedback.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* refactor: streamline WorkflowAPPForm and template status logic

- Removed unused state and effects related to user switching and status tracking, simplifying the component's logic.
- Updated polling logic to dynamically control based on template content and status, enhancing performance.
- Refactored shouldShowStatusBadge function to eliminate unnecessary parameters, focusing on essential status checks.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* feat: add TemplateEditorSkeleton component for improved user experience

- Introduced TemplateEditorSkeleton to provide a skeleton loading screen during template generation states, enhancing user feedback.
- Updated WorkflowAPPForm to conditionally render the skeleton based on template content and status, improving responsiveness.
- Ensured compliance with coding standards, including the use of optional chaining and nullish coalescing for safer property access.

* refactor: update WorkflowAPPForm logic for form and skeleton display

- Enhanced the conditions for displaying the form and skeleton screen based on template status and content availability, improving user interaction.
- Clarified comments to better explain the logic for showing the editor, form, and skeleton states.
- Ensured compliance with coding standards, including the use of optional chaining and nullish coalescing for safer property access.

* feat: rm Status badge

* feat: add updating message to i18n translations

- Introduced a new translation string for the updating state in both English and Chinese, enhancing user feedback during template updates.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* refactor: remove status badge from WorkflowAPPForm

- Eliminated the status badge from the top-right corner of the WorkflowAPPForm, streamlining the component's display logic.
- Ensured compliance with coding standards, including the use of optional chaining and nullish coalescing for safer property access.

* refactor: remove unused template status badge and related logic

- Deleted the TemplateStatusBadge component and its references from WorkflowAPPForm, simplifying the component structure and improving maintainability.
- Updated i18n translations by removing outdated template status messages, retaining only the necessary updating message for clarity.
- Ensured compliance with coding standards, including the use of optional chaining and nullish coalescing for safer property access.

* feat: add prompt caching to bedrock provider (#1822)

feat: Add prompt caching support with cache write token tracking

- Add cacheWriteTokens field to TokenUsageItem schema for tracking cache creation tokens
- Implement cache token extraction from AWS Bedrock and Anthropic usage metadata
- Support multiple field name variations for cache tokens across different providers
- Fix token calculation logic to correctly handle cache read/write tokens separately
- Add logging for cache hit events with detailed token breakdown
- Update provider dependencies to support new caching features
- Refactor message content handling with proper type assertions

* fix: Remove duplicate token credit usage (#1835)

* feat: enhance subscription store and workflow app integration (#1837)

- Updated the subscription store to manage credit insufficient modal visibility with improved state handling.
- Integrated subscription store into WorkflowAppPage to conditionally display error messages based on modal visibility.
- Enhanced WorkflowAPPForm to check credit balance before executing workflows, ensuring better user feedback and experience.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* fix: restore original PriceContent style & prioritize vouchers with Stripe promo code (#1843)

* refactor: remove `PricingModal` component and integrate pricing display into `priceContent` and `subscribe-modal` components.

* feat: Make voucher expiration configurable via `app.config` and reduce the daily popup trigger limit.

* Fix/optimize shift tab animate (#1836)

* fix: Reduce template list opacity transition duration from 300ms to 1ms.

* feat: add GithubStar component to TopToolbar for enhanced user engagement

- Integrated the GithubStar component into the TopToolbar, improving visibility for users to engage with the project.
- Ensured compliance with coding standards, including the use of optional chaining and nullish coalescing for safer property access.

* refactor: update avatar handling and improve user profile management … (#1840)

* refactor: update avatar handling and improve user profile management in AccountSetting component

- Changed avatarKey state initialization to use undefined instead of an empty string for better type safety.
- Updated avatarStorageKey assignment to use nullish coalescing for handling undefined values.
- Simplified userProfile handling by setting avatarKey to undefined directly, ensuring consistent state management.
- Enhanced component performance by adhering to coding standards, including optional chaining and nullish coalescing for safer property access.

* feat: enhance chat box and copilot message components with telemetry logging

- Updated handleSendMessage in ChatBox to log events when messages are sent, capturing the source of the action.
- Modified handleRetry in CopilotMessage to include logging for retry actions, improving event tracking.
- Removed redundant logEvent call in ChatActions to streamline the code.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* fix: update logging property name in CopilotMessage component

- Changed the property name from 'source_type' to 'source' in the telemetry logging for retry button clicks, improving clarity and consistency in event tracking.
- Ensured compliance with coding standards, including the use of optional chaining and nullish coalescing for safer property access.

* feat: Add auto model routing info in action result (#1838)

add auto model routing info in action result

* feat: enhance PlanItem component with login handling and button state… (#1844)

feat: enhance PlanItem component with login handling and button state management

- Added source prop to PlanItem for conditional navigation during login.
- Improved button state logic to differentiate between logged-in and guest users.
- Updated button text to show "Get Started" for non-logged-in users.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* feat(workflow-app): optimize cover image upload with compression and preloading (#1842)

* fix: Reduce template list opacity transition duration from 300ms to 1ms.

* feat: replace banner.svg with banner.png for improved image handling in workflow app

* feat: replace banner.png with banner.webp for improved image format in workflow app

* feat: enhance image compression utility with WebP support and format handling

- Added support for WebP format in image compression options.
- Implemented a check for WebP support in the user's browser.
- Updated canvasToBlob function to handle different image formats based on availability.
- Improved file naming logic to reflect the actual image format used during compression.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* feat: Support credit billing for cached tokens (#1848)

credit usage for cache

* feat:  Add file-type User Input support with optional handling and publish validation (#1847)

* feat: Add validation for required file inputs, track missing optional file inputs, and enable auto-editing of variables with error display.

* feat: Validate required workflow variables for completeness before template publishing, add i18n messages, and include a file input implementation plan document.

* feat: Enhance variable extraction to include a default `required` field for all variables, improve resource type detection, and document file input implementation.

* feat: enhances observability and tracing infrastructure (#1846)

* WIP[3]: verify langfuse trace data completeness

- Add toolDefinitions, systemPrompt, modelConfig to agent invoke metadata
- langfuse-sdk v4 OpenTelemetry integration auto-records metadata to Generation
- Fix api-server.sh restart orphan process issue

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* WIP[4]: remove redundant metadata fields (systemPrompt, modelConfig)

Phase 6: Optimize Langfuse trace payload size

Changes:
- Remove systemPrompt from metadata (already in input[0])
- Remove modelConfig from metadata (duplicates modelParameters)
- Silence console.log in tracer/providers (Phase 5 cleanup)

Results:
- Metadata size reduced from ~9.7KB to ~4.4KB per generation (-54%)
- Total savings: ~10KB per agent invocation (2 generations)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* feat(observability): filter internal metadata from Langfuse traces

Reduce Langfuse trace payload size by filtering out redundant metadata:

- Add FilteredLangfuseCallbackHandler to remove LangGraph/LangChain
  internal fields (langgraph_*, ls_*, __pregel_*) that duplicate
  top-level Langfuse fields or are not useful for trace analysis
- Slim down resourceAttributes in tracer.ts mask function, keeping
  only service.name, host.name, and process.runtime.version
- Use new filtered handler in skill-invoker.service.ts

This reduces Generation metadata from ~27 keys to ~15 keys per trace.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

---------

Co-authored-by: HanYuanxi <[email protected]>
Co-authored-by: Claude Opus 4.5 <[email protected]>

* fix: preserve active credit recharge records after cancellation (#1849)

refactor: remove credit recharge expiration logic from subscription service

- Removed the logic for expiring active credit recharge records upon subscription cancellation to streamline the process.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* feat: restore canvas duplication for new system (#1850)

feat: Enable canvas and workflow duplication/sharing UI features, improve template generation enqueueing, enhance drive file duplication logic, and add image processing research documents.

* fix: update default subscription interval to yearly (#1851)

fix: update default subscription interval in PlanItem component from monthly to yearly

* fix: enhance object storage error handling and object key normalization (#1852)

- Added `normalizeObjectKey` method to MinioStorageBackend to ensure object keys are formatted correctly.
- Introduced `isNotFoundError` method to handle various error cases consistently when accessing MinIO.
- Updated methods in MinioStorageBackend to utilize normalized keys and improved error logging.
- Refactored DriveService to handle missing storage keys and stream more robustly, throwing appropriate exceptions when necessary.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* optimize to truncate the context and tool output

* feat: Enhance context management and compression for agent tools

- Introduced ToolType for post-processing selection in AgentBaseTool.
- Added 'copilot' model scene and corresponding default model configuration.
- Implemented archiving options in UpdateWorkflowVariablesRequest and UpsertDriveFileRequest.
- Added ArchivedRef and context management utilities for efficient message handling.
- Developed compressAgentLoopMessages function to manage context during agent iterations.
- Enhanced context block truncation and archived references handling.
- Updated utility exports to include context-manager functionalities.

* feat: Update link filtering constants to allow more diverse results

* feat: Enhance ComposioService and Token utilities for improved context handling and compression

- Updated ComposioService to include 'heygen' as a valid integration for generic API key usage.
- Refactored langchainConfig type casting to ensure compatibility.
- Modified result handling to return full result object instead of a simplified error message.
- Removed estimateTokens and estimateMessagesTokens functions from token utilities to streamline token counting.
- Integrated countToken and countMessagesTokens for precise token calculations in various contexts.
- Enhanced Agent class to calculate tool tokens for better budget estimation during agent loops.
- Adjusted context manager to implement cache-friendly compression strategies, preserving essential message context.
- Implemented truncation logic for ToolMessages to fit within budget constraints.
- Updated pnpm-lock.yaml to reflect dependency changes.

* feat: Add 'nodejs' to Language type for improved language support

* feat: refactor sharing to shared-voucher mode with payment-based rewards (#1856)

* feat: Enhance voucher invitation and sharing logic with improved verification, claimant usage, owner rewards, updated popup limit, and new documentation.

* docs: Add voucher refactor plan and image processing research documents, and reduce daily voucher popup trigger limit.

* fix: optimize FilePreview performance with content truncation and rendering improvements (#1857)

* feat: Enhance file preview with expanded language and file type support, markdown truncation, and detailed workflow abort logging.

* feat: decrease file preview truncation limits and use CodeViewer for code previews

* feat: Increase the maximum number of preview lines for markdown and code files from 1000 to 2000.

* refactor: Translate comments in file preview components from Chinese to English.

* feat: add ResetPasswordModal to login page (#1855)

- Integrated ResetPasswordModal component into the LoginPage for enhanced user experience.
- Ensured compliance with coding standards, including proper component structure and styling with Tailwind CSS.

* feat(workflow-app): preserve workflow products when ending execution  (#1854)

* feat(workflow-app): enhance execution polling and URL management

- Updated error handling in useWorkflowExecutionPolling to include data error codes.
- Refactored WorkflowAppPage to retrieve executionId from URL query parameters and manage it with useSearchParams.
- Introduced a helper function to update executionId in the URL, maintaining it for debugging purposes.
- Improved performance by using refs to store stopPolling and previous shareId, preventing unnecessary re-renders.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* feat(workflow-app): improve runtime file fetching logic during execution

- Updated the logic to fetch drive files for runtime products to include fetching during execution when nodes are finished.
- Simplified the condition to check for canvasId and removed unnecessary checks for isRunning.
- Enhanced the dependency array in useEffect to include nodeExecutions for better performance and accuracy.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* fix: cache points for bedrock prompt caching (#1858)

- Implemented two-level caching strategy:
  - Global static point: caches system prompt across all sessions (index 0)
  - Session dynamic points: caches recent conversation history (last 3 messages before user query)
- Replaced `instanceof` checks with `_getType()` method to properly handle deserialized messages
- Added support for caching AI messages with tool calls

* feat: add invite redirect page (#1859)

* feat: Implement invite redirection to handle voucher codes, preserve invite parameters during redirects, and rename tool call ID generation.

* fix: retrieve invite code from 'invite' query parameter instead of 'code'

* feat: implement pending redirect storage for payment and OAuth flows (#1860)

- Added `storePendingRedirect` functionality across multiple components to preserve the current page for redirection after payment and OAuth callbacks.
- Updated `CreditPacksModal`, `PriceContent`, `CreditInsufficientModal`, `VoucherPopup`, `LoginModal`, and `LoginPage` to utilize the new redirect storage mechanism.
- Enhanced `useHandleUrlParamsCallback` to manage pending redirects effectively after payment success or OAuth login.

* fix: Standardize invite link path to `/invite` and enhance invite redirection with SPA navigation.

* feat: Implement invite redirection to handle voucher codes, preserve invite parameters during redirects, and rename tool call ID generation.

* fix: retrieve invite code from 'invite' query parameter instead of 'code'

* feat: Standardize invite link path to `/invite` and enhance invite redirection with SPA navigation.

* Fix/Adjust plan priority (#1853)

* fix: update default subscription interval in PlanItem component from monthly to yearly

* fix: update PlanPriorityMap to correct starter plan priority from 1 to 2

* feat: implement tool-based auto model routing (#1861)

- Add tool-based routing logic to AutoModelRouter with configurable target tools and model selection
- Extend RouterContext with scene and toolsets fields for routing decision
- Add getToolBasedRoutingConfig utility to read routing config from environment variables
- Integrate tool-based routing in SkillService and WorkflowService by passing scene and toolsets context

* feat: implement global audio manager and default agent title fallback (#1863)

* feat: implement global audio manager for single audio playback control

- Added AudioManager class to manage audio playback, ensuring only one audio plays at a time.
- Integrated audio manager with AudioRenderer component to register audio elements and handle play/pause events.
- Ensured compliance with coding standards, including proper use of hooks and Tailwind CSS for styling.

* feat: add default agent title to translations for English and Chinese

- Added 'defaultAgentTitle' key to the translations in both English and Chinese files to provide a fallback title for agents.
- Updated WorkflowAppPage to utilize the new translation key when rendering node titles and current step titles, ensuring a consistent user experience.
- Ensured compliance with coding standards, including optional chaining and nullish coalescing for safer property access.

* fix: update unauthenticated invite redirect to use a full page reload.

* feat: Implement invite redirection to handle voucher codes, preserve invite parameters during redirects, and rename tool call ID generation.

* fix: retrieve invite code from 'invite' query parameter instead of 'code'

* feat: Standardize invite link path to `/invite` and enhance invite redirection with SPA navigation.

* feat: update unauthenticated invite redirect to use a full page reload.

* enhance token estimation and truncation methods for improved performance

feat: enhance token estimation and truncation methods for improved performance

* enhance document generation by adding fileId placeholder support

feat: enhance document generation by adding fileId placeholder support and URL replacement

* fix: update translation queries in English and Chinese (#1870)

* Updated query1 in both English and Chinese translation files to reflect new instructions for using Perplexity to analyze YouTube creators related to a product.
* Ensured compliance with coding standards, including proper string literal usage and translation consistency.

* feat: Add invite page to public access pages whitelist (#1874)

feat: Add invite page check to `useIsSharePage` hook, ignore `.serena` files, and configure SCSS formatter in VS Code.

* refactor: update default selected credit pack in CreditInsufficientModal (#1876)

* Removed credit pack options for 100 and 500 credits.
* Changed default selected credit pack from 100 to 1000 for users with a paid subscription.
* Ensured consistency in credit pack options displayed for users.

* feat: remove file type restrictions and fix unsupported file download                                                                                                              (#1883)

feat: Enhance resource import by preventing save during upload, add file download loading states, and configure Serena project settings.

---------

Co-authored-by: lefarcen <[email protected]>
Co-authored-by: Siri-Ray <[email protected]>
Co-authored-by: Claude Opus 4.5 <[email protected]>
Co-authored-by: nettee <[email protected]>
Co-authored-by: orbisai0security <[email protected]>
Co-authored-by: orbisai0security <[email protected]>
Co-authored-by: Achieve <[email protected]>
Co-authored-by: Sophia <[email protected]>
Co-authored-by: PerishFire <[email protected]>
Co-authored-by: HanYuanxi <[email protected]>
Co-authored-by: a1chzt <[email protected]>
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