Skip to content

Feat:Add and complete small models and robot meta-services#1639

Merged
hexqi merged 5 commits intoopentiny:developfrom
lichunn:feat/ai-service
Sep 23, 2025
Merged

Feat:Add and complete small models and robot meta-services#1639
hexqi merged 5 commits intoopentiny:developfrom
lichunn:feat/ai-service

Conversation

@lichunn
Copy link
Collaborator

@lichunn lichunn commented Sep 19, 2025

English | 简体中文

PR

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our Commit Message Guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)
  • Built its own designer, fully self-validated

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

Background and solution

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Summary by CodeRabbit

  • New Features

    • Centralized robot settings via a shared hook/service for consistent model, API key and mode selection across the UI.
    • Added "complete model" selection with persistence for both existing and custom model flows.
  • Refactor

    • Removed the maxTokens/context-length field from UI and flows.
    • Standardized AI mode identifiers (e.g., default to "chat") and unified model selection/state handling.
  • Documentation

    • Updated docs and examples to reflect the new configuration pattern and removed maxTokens from samples.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 19, 2025

Walkthrough

Centralizes robot plugin state and APIs behind a new useRobot hook and RobotService, refactors components to consume useRobot and reactive robotSettingState (adding completeModel), removes maxTokens from model definitions and UI, updates docs to reference useRobot.ts, and registers the hook and types in the registry.

Changes

Cohort / File(s) Summary
Docs reference update
docs/extension-capabilities-tutorial/ai-plugin-configuration.md
Update docs to reference .../useRobot.ts instead of .../robotSetting.js.
Plugin service & registration
packages/plugins/robot/index.ts, packages/plugins/robot/src/js/index.ts, packages/register/src/hooks.ts, packages/register/src/types.ts
Add RobotService meta service exporting useRobot apis; add metas: [RobotService] to plugin index; register HOOK_NAME.useRobot and export useRobot(...); add UseRobotApi type alias and hook registry entry.
Hook implementation & public API reshape
packages/plugins/robot/src/js/useRobot.ts
Convert many named exports into internal constants and provide a default-exported factory that returns the public API; introduce reactive robotSettingState.selectedModel.completeModel; remove maxTokens from AIModelOptions; add AI_MODES; internalize helper functions and expose them via the default export.
Component refactors to consume useRobot
packages/plugins/robot/src/Main.vue, packages/plugins/robot/src/RobotSettingPopover.vue, packages/plugins/robot/src/RobotTypeSelect.vue
Replace legacy imports/state with useRobot() and robotSettingState; Main.vue now reads/writes model, apiKey, baseUrl, and visual-mode logic from robotSettingState; RobotSettingPopover.vue removes maxTokens UI/logic, adds completeModel fields and localStorage persistence; RobotTypeSelect.vue sources AI_MODES from useRobot() and changes default aiType to 'chat'.
Docs example update
docs/advanced-features/new-ai-plugin-usage.md
Remove maxTokens from example model entries (deepseek-chat, deepseek-reasoner).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant App
  participant Plugin as Robot Plugin
  participant Registry as Meta Registry
  participant RobotService as RobotService (meta)

  User->>App: Open application
  App->>Plugin: Load robot plugin
  Plugin->>Registry: register metas (RobotService)
  Registry-->>Plugin: registration confirmed
  Note right of RobotService #DDEBF7: RobotService.composable -> useRobot
Loading
sequenceDiagram
  autonumber
  participant Component as Vue Component
  participant Hooks as exported useRobot
  participant Registry as Hook Resolver
  participant Service as RobotService.apis
  participant Impl as useRobot implementation

  Component->>Hooks: call useRobot()
  Hooks->>Registry: getHook(HOOK_NAME.useRobot)
  Registry->>Service: resolve composable
  Service->>Impl: invoke composable
  Impl-->>Component: return { robotSettingState, AIModelOptions, AI_MODES, helpers }
  Component->>Component: bind UI, persist completeModel, send requests using robotSettingState
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • chilingling

Poem

I nibble keys and tweak the hook,
robotSettingState now leads the book.
completeModel hops into place,
maxTokens gone — a tidier space.
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 title is concise and directly reflects the primary work in the changeset—adding small models and robot meta-services—which aligns with the added RobotService, useRobot hook, and AI model option updates in the diff; it is clear enough for a reviewer scanning PR history.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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.

@github-actions github-actions bot added documentation Improvements or additions to documentation enhancement New feature or request labels Sep 19, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
packages/plugins/robot/src/js/useRobot.ts (2)

13-13: Consider updating the meta comment to reflect the actual service path.

The comment references engine.plugins.robot.useRobot, but according to the RobotService definition, the actual ID is engine.service.robot.

-/* metaService: engine.plugins.robot.useRobot */
+/* metaService: engine.service.robot */

119-132: Improve error handling transparency.

The error message "获取block列表失败" (Failed to fetch block list) doesn't provide enough context. Consider including the actual error details for better debugging.

   } catch (err) {
     // 捕获错误
-    throw new Error('获取block列表失败', { cause: err })
+    throw new Error(`获取block列表失败: ${err.message}`, { cause: err })
   }
packages/register/src/hooks.ts (1)

68-69: Consider removing or translating the Chinese comment.

The comment "// 自定义" (meaning "// custom") doesn't provide additional context beyond what's already clear from the code. Either remove it or translate it to English for consistency.

  [HOOK_NAME.useCustom]: {},
- [HOOK_NAME.useRobot]: {} // 自定义
+ [HOOK_NAME.useRobot]: {}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 898ab86 and 7bb3cd4.

📒 Files selected for processing (9)
  • docs/extension-capabilities-tutorial/ai-plugin-configuration.md (1 hunks)
  • packages/plugins/robot/index.ts (1 hunks)
  • packages/plugins/robot/src/Main.vue (11 hunks)
  • packages/plugins/robot/src/RobotSettingPopover.vue (3 hunks)
  • packages/plugins/robot/src/RobotTypeSelect.vue (2 hunks)
  • packages/plugins/robot/src/js/index.ts (1 hunks)
  • packages/plugins/robot/src/js/useRobot.ts (4 hunks)
  • packages/register/src/hooks.ts (4 hunks)
  • packages/register/src/types.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-01-14T08:42:18.574Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1038
File: packages/plugins/block/index.js:24-24
Timestamp: 2025-01-14T08:42:18.574Z
Learning: In the tiny-engine project, breaking changes are documented in the changelog rather than in JSDoc comments or separate migration guides.

Applied to files:

  • docs/extension-capabilities-tutorial/ai-plugin-configuration.md
📚 Learning: 2025-01-14T06:55:59.692Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1011
File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:95-98
Timestamp: 2025-01-14T06:55:59.692Z
Learning: The tiny-select component from opentiny/vue library ensures selected options are valid internally, requiring no additional validation in the change handler.

Applied to files:

  • packages/plugins/robot/src/RobotSettingPopover.vue
📚 Learning: 2025-01-14T06:49:00.797Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1011
File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:63-73
Timestamp: 2025-01-14T06:49:00.797Z
Learning: In the tiny-engine project, the SvgIcon component is globally registered and available throughout Vue components without requiring explicit imports.

Applied to files:

  • packages/plugins/robot/src/Main.vue
🧬 Code graph analysis (5)
packages/register/src/types.ts (1)
packages/plugins/robot/src/js/index.ts (1)
  • RobotService (4-11)
packages/plugins/robot/index.ts (1)
packages/plugins/robot/src/js/index.ts (1)
  • RobotService (4-11)
packages/plugins/robot/src/js/index.ts (1)
packages/register/src/hooks.ts (2)
  • useRobot (97-97)
  • HOOK_NAME (22-44)
packages/register/src/hooks.ts (1)
packages/register/src/types.ts (1)
  • UseRobotApi (27-27)
packages/plugins/robot/src/js/useRobot.ts (1)
packages/register/src/common.ts (1)
  • getOptions (32-34)
⏰ 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: push-check
🔇 Additional comments (19)
docs/extension-capabilities-tutorial/ai-plugin-configuration.md (1)

19-19: Documentation reference correctly updated to TypeScript file.

The path update from robotSetting.js to useRobot.ts aligns with the new hook-based architecture.

packages/plugins/robot/src/js/useRobot.ts (1)

177-198: Well-structured module export pattern.

The default export factory pattern provides a clean API surface and good encapsulation of the internal state.

packages/register/src/types.ts (2)

14-14: Type import path should use package name.

The import uses a relative path to access the robot plugin. Consider using the package name for consistency with other imports.

-import type { RobotService } from '@opentiny/tiny-engine-plugin-robot'
+import type { RobotService } from '@opentiny/tiny-engine-plugin-robot'

Note: The import is already using the package name. This is correct.


27-27: Type alias follows consistent pattern.

The new UseRobotApi type follows the established naming convention and correctly references the service's API surface.

packages/plugins/robot/index.ts (1)

17-23: Plugin metadata properly exposed.

The addition of metas: [RobotService] correctly registers the robot service with the plugin system, following the established pattern.

packages/register/src/hooks.ts (3)

18-19: LGTM!

The addition of UseRobotApi to the imports and useRobot to the exports aligns well with the PR's goal of centralizing robot plugin state and APIs. The implementation follows the established pattern for hook registration.


42-43: LGTM!

The robot hook is correctly added to HOOK_NAME constant following the existing naming convention.


97-97: LGTM!

The useRobot hook is correctly exposed following the established pattern with proper typing.

packages/plugins/robot/src/RobotTypeSelect.vue (3)

48-48: LGTM!

Good architectural improvement - switching from local imports to the centralized useRobot registry hook aligns with the PR's goal of centralizing robot state management.


64-64: LGTM!

The constants are properly destructured from useRobot() and correctly returned for template usage, maintaining the component's functionality while adopting the centralized state management pattern.

Also applies to: 83-85


59-59: Default prop changed to literal 'talk' — no break detected.

Repo search shows RobotTypeSelect is only used in packages/plugins/robot/src/Main.vue where :aiType is passed (aiType is initialized to TALK_TYPE from useRobot, and useRobot defines TALK_TYPE = 'talk'), so current usage is unaffected.

packages/plugins/robot/src/RobotSettingPopover.vue (3)

86-86: LGTM!

Good migration to the centralized registry-based approach via useRobot, replacing the local module import.


107-107: LGTM!

The destructuring of EXISTING_MODELS, CUSTOMIZE, and getAIModelOptions from useRobot() properly replaces the previous local imports, maintaining functionality while using the centralized API.


196-207: Good cleanup of the component's public API.

The removal of changeModel and maxTokensTip from the return object, along with the removal of maxTokens-related form fields, simplifies the component interface. This aligns with the centralized state management approach where model changes are likely handled at a higher level.

packages/plugins/robot/src/Main.vue (5)

127-134: LGTM!

The import changes properly consolidate robot-related functionality under the centralized useRobot hook from the meta-register, aligning with the PR's architectural goals.


191-201: Excellent centralization of robot state management.

The destructuring of all robot-related constants and robotSettingState from useRobot() successfully centralizes the state management. This is a significant architectural improvement that reduces coupling and makes the state more maintainable.


28-28: Consistent use of centralized robotSettingState.

All references to the selected model have been correctly updated to use robotSettingState.selectedModel instead of the local state. This ensures the state remains synchronized across all components using the robot functionality.

Also applies to: 77-78, 237-237


575-578: LGTM - Model management properly centralized.

The initCurrentModel and changeModel functions now correctly operate on robotSettingState.selectedModel, ensuring model changes are reflected globally across all components using the robot service.

Also applies to: 625-654


814-814: LGTM!

The component properly exposes robotSettingState instead of the local selectedModel, maintaining consistency with the centralized state management approach.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
docs/advanced-features/new-ai-plugin-usage.md (2)

26-26: Typo: “MC工具按钮” → “MCP工具按钮”.

Apply:

- - **MC工具按钮**:管理和配置MCP工具的入口
+ - **MCP工具按钮**:管理和配置MCP工具的入口

111-111: Correct acronym expansion: MCP = Model Context Protocol (not Model Connector Protocol).

Apply:

- MCP(Model Connector Protocol)是新版AI插件的核心功能之一。
+ MCP(Model Context Protocol)是新版AI插件的核心功能之一。
packages/plugins/robot/src/RobotSettingPopover.vue (1)

204-210: Consider null-safe access for options lookup.

When AIModelOptions.find() returns undefined (e.g., when baseUrl doesn't match any option), accessing options?.model and options?.completeModel correctly returns undefined, but this leaves state.modelOptions and state.completeModelOptions as undefined instead of empty arrays.

Consider providing default empty arrays for consistency:

     const options = AIModelOptions.find((option) => option.value === state.existFormData.baseUrl)
-    state.modelOptions = options?.model
-    state.completeModelOptions = options?.completeModel || []
+    state.modelOptions = options?.model || []
+    state.completeModelOptions = options?.completeModel || []
packages/plugins/robot/src/js/useRobot.ts (1)

80-80: Add validation for completeModel array access.

When getAIModelOptions()[0].completeModel is undefined or empty, accessing [0]?.value correctly returns undefined, but consider whether an empty string is the intended fallback for all cases.

Consider extracting the initialization logic for better maintainability:

+const getDefaultModelConfig = () => {
+  const options = getAIModelOptions()
+  const firstOption = options[0]
+  return {
+    label: firstOption.label,
+    activeName: EXISTING_MODELS,
+    baseUrl: firstOption.value,
+    model: firstOption.model[0].value,
+    completeModel: firstOption.completeModel?.[0]?.value || '',
+    apiKey: ''
+  }
+}
+
 const robotSettingState = reactive({
-  selectedModel: {
-    label: getAIModelOptions()[0].label,
-    activeName: EXISTING_MODELS,
-    baseUrl: getAIModelOptions()[0].value,
-    model: getAIModelOptions()[0].model[0].value,
-    completeModel: getAIModelOptions()[0].completeModel[0]?.value || '',
-    apiKey: ''
-  }
+  selectedModel: getDefaultModelConfig()
 })
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7bb3cd4 and cd2ed15.

📒 Files selected for processing (4)
  • docs/advanced-features/new-ai-plugin-usage.md (1 hunks)
  • packages/plugins/robot/src/Main.vue (10 hunks)
  • packages/plugins/robot/src/RobotSettingPopover.vue (8 hunks)
  • packages/plugins/robot/src/js/useRobot.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-01-14T06:49:00.797Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1011
File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:63-73
Timestamp: 2025-01-14T06:49:00.797Z
Learning: In the tiny-engine project, the SvgIcon component is globally registered and available throughout Vue components without requiring explicit imports.

Applied to files:

  • packages/plugins/robot/src/Main.vue
📚 Learning: 2025-01-14T06:55:59.692Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1011
File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:95-98
Timestamp: 2025-01-14T06:55:59.692Z
Learning: The tiny-select component from opentiny/vue library ensures selected options are valid internally, requiring no additional validation in the change handler.

Applied to files:

  • packages/plugins/robot/src/RobotSettingPopover.vue
🧬 Code graph analysis (1)
packages/plugins/robot/src/js/useRobot.ts (1)
packages/register/src/common.ts (1)
  • getOptions (32-34)
⏰ 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: push-check
🔇 Additional comments (4)
docs/advanced-features/new-ai-plugin-usage.md (2)

68-70: Add migration note for deprecated maxTokens & verify repo-wide cleanup

maxTokens removal is correct — add the migration hint below the model-list snippet and confirm no lingering references.

Location: docs/advanced-features/new-ai-plugin-usage.md — insert after the model list (≈ lines 68–70).

Add:

> 迁移提示:自 v2.8 起,模型项不再支持/需要 maxTokens 字段;插件会基于模型/服务端能力自动处理上下文与长度控制。旧配置中的该字段将被忽略。

Verify with:

rg -nP -S '\bmaxTokens\b' -uu
rg -nP -S '\bmax_tokens\b' -uu
rg -nP -S -i 'maxtokens' -uu
rg -nP -S 'customCompatibleAIModels' docs

If matches are found, update/remove those occurrences and re-run the searches.


19-20: Verify referenced images exist and paths are correct.
Automated sandbox checks failed; manually confirm the images referenced in docs/advanced-features/new-ai-plugin-usage.md (lines 19-20, 28-29, 36-37, 43-49, 124-139, 199-203) exist under docs/advanced-features/imgs/ (./imgs) or update the paths. Quick local check: ls -la docs/advanced-features/imgs/

packages/plugins/robot/src/Main.vue (1)

368-372: Security: Remove client-side VITE_API_TOKEN fallback to prevent secret exposure.

The code falls back to import.meta.env.VITE_API_TOKEN when robotSettingState.selectedModel.apiKey is unavailable. VITE_* environment variables are embedded in frontend bundles and must not contain secrets. This exposes sensitive API keys to anyone who can access your application's JavaScript.

Remove the client-side fallback and require explicit API key configuration:

-            Authorization: `Bearer ${robotSettingState.selectedModel.apiKey || import.meta.env.VITE_API_TOKEN}`
+            Authorization: `Bearer ${robotSettingState.selectedModel.apiKey}`

Add validation to reject requests when no API key is provided:

 const sendStreamRequest = async () => {
   const requestData = getSendSeesionProcess()
+  if (!robotSettingState.selectedModel.apiKey) {
+    messages.value[messages.value.length - 1].content = '请先配置 API Key'
+    inProcesing.value = false
+    return
+  }
   if (useMcpServer().isToolsEnabled && aiType.value === TALK_TYPE) {

Also applies to line 469.

packages/plugins/robot/src/RobotSettingPopover.vue (1)

150-158: Incorrect — both model defaults already use fallbacks
Both assignments use optional chaining with an empty-string fallback: state.existFormData.model = state.modelOptions[0]?.value || '' and state.existFormData.completeModel = state.completeModelOptions[0]?.value || '' (packages/plugins/robot/src/RobotSettingPopover.vue lines 153–157).

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/plugins/robot/src/Main.vue (2)

701-733: Null‑safety and types for file selection; fix potential crash on files === null

files can be null; accessing files.length will throw. Retry passes a File, not a FileList. Normalize types and guards.

-    const handleSingleFilesSelected = (files: FileList | null, retry = false) => {
+    const handleSingleFilesSelected = (files: FileList | File | null, retry = false) => {
       if (retry) {
         singleAttachmentItems.value[0].status = 'uploading'
         singleAttachmentItems.value[0].isUploading = true
         singleAttachmentItems.value[0].messageType = 'uploading'
       } else {
-        if (!files.length) return
+        if (!files || (files as FileList).length === 0) return
@@
-        if (files && files.length > 1) {
+        if ((files as FileList).length > 1) {
@@
-        if (files && files.length > 0) {
-          // 将选中的文件转换为 Attachment 格式并添加到附件列表
-          const newAttachments = Array.from(files).map((file) => ({
+        if ((files as FileList).length > 0) {
+          const list = Array.from(files as FileList)
+          const newAttachments = list.map((file) => ({
             size: file.size,
             rawFile: file
           }))
           singleAttachmentItems.value.push(...newAttachments)
         }
       }
@@
-      const fileData = retry ? files : files[0]
+      const fileData = retry ? (files as File) : (files as FileList)[0]

229-243: Security: stop persisting API keys in localStorage — remove apiKey from persisted session and source it from in-memory state

Confirmed: Main.vue writes robotSettingState.selectedModel (including apiKey) to localStorage 'aiChat' and RobotSettingPopover stores model info; requests also read apiKey from stored foundationModel — this exposes secrets to XSS and must be fixed.

Actions (minimal):

  • Do not persist apiKey into localStorage when writing 'aiChat'. (packages/plugins/robot/src/Main.vue — setContextSession(), ~lines 229-236)
  • Use in-memory apiKey for requests instead of requestData.foundationModel?.apiKey (packages/plugins/robot/src/Main.vue — params ~393-399; Authorization headers ~368-372 and ~466-469).
  • When hydrating from storage, merge non-sensitive fields and preserve any in-memory apiKey (initCurrentModel, ~572-576).
  • Prevent RobotSettingPopover from persisting apiKey into 'AICompleteModel' (packages/plugins/robot/src/RobotSettingPopover.vue ~174-199).
  • After changes, verify changeApiKey()/endContent() flows still function (they currently clear and recreate session).

Suggested patch (apply where shown):

@@
-              foundationModel: {
-                ...robotSettingState.selectedModel
-              },
+              // Avoid persisting secrets in localStorage
+              foundationModel: (({ apiKey, ...rest }) => rest)(robotSettingState.selectedModel || {}),
@@
-          apiKey: requestData.foundationModel?.apiKey,
+          // Source from memory, not storage
+          apiKey: robotSettingState.selectedModel?.apiKey,
@@
-      robotSettingState.selectedModel = {
-        ...JSON.parse(aiSession)?.foundationModel
-      }
+      // Preserve any existing in-memory apiKey while hydrating non-sensitive fields
+      robotSettingState.selectedModel = {
+        ...robotSettingState.selectedModel,
+        ...JSON.parse(aiSession)?.foundationModel
+      }
🧹 Nitpick comments (2)
packages/plugins/robot/src/Main.vue (2)

624-627: Also compare completeModel when deciding to clear session

If completeModel affects behavior, include it in the change detection to avoid stale sessions.

-      if (
-        robotSettingState.selectedModel.baseUrl !== model.baseUrl ||
-        robotSettingState.selectedModel.model !== model.model
-      ) {
+      if (
+        robotSettingState.selectedModel.baseUrl !== model.baseUrl ||
+        robotSettingState.selectedModel.model !== model.model ||
+        robotSettingState.selectedModel.completeModel !== model.completeModel
+      ) {

Also applies to: 637-637


735-740: Avoid manually setting multipart Content-Type for FormData

Let the browser set the boundary; forcing Content-Type can break uploads in some environments.

-          .post('/material-center/api/resource/upload', formData, {
-            headers: {
-              'Content-Type': 'multipart/form-data'
-            }
-          })
+          .post('/material-center/api/resource/upload', formData)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd2ed15 and de0969a.

📒 Files selected for processing (1)
  • packages/plugins/robot/src/Main.vue (10 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-01-14T06:49:00.797Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1011
File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:63-73
Timestamp: 2025-01-14T06:49:00.797Z
Learning: In the tiny-engine project, the SvgIcon component is globally registered and available throughout Vue components without requiring explicit imports.

Applied to files:

  • packages/plugins/robot/src/Main.vue
⏰ 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: push-check
🔇 Additional comments (6)
packages/plugins/robot/src/Main.vue (6)

75-78: Upload gating by model and mode — LGTM

Only allowing a single image when the selected model is visual and in BUILD mode is correct and consistent with downstream handling.


126-133: Adopting meta-register hooks — LGTM

Importing useRobot/useHistory/useCanvas/useModal/getMetaApi centralizes deps and improves cohesion.


190-201: Centralized robot state via useRobot — LGTM

Destructuring robotSettingState and constants from useRobot aligns this component with the new state model.


624-627: Fix comparison bug — LGTM

Comparing baseUrl and model strings against the incoming model is correct and resolves the always-true condition.


647-652: Credential-only updates handled in-place — LGTM

Updating apiKey without resetting when baseUrl/model are unchanged is correct.


368-373: Security — remove client-side fallback to VITE_API_TOKEN; fail-fast when missing apiKey

VITE_* variables are baked into the bundle and must not hold secrets. Using them as a fallback risks unauthorized API usage. Require an explicit apiKey and abort when absent.

Apply:

@@
-          await sendMcpRequest(messages.value, {
-            model: robotSettingState.selectedModel.model,
-            headers: {
-              Authorization: `Bearer ${robotSettingState.selectedModel.apiKey || import.meta.env.VITE_API_TOKEN}`
-            },
-            baseUrl: robotSettingState.selectedModel.baseUrl
-          })
+          await sendMcpRequest(messages.value, {
+            model: robotSettingState.selectedModel.model,
+            headers: {
+              Authorization: `Bearer ${robotSettingState.selectedModel.apiKey}`
+            },
+            baseUrl: robotSettingState.selectedModel.baseUrl
+          })
@@
-          {
-            Authorization: `Bearer ${robotSettingState.selectedModel.apiKey || import.meta.env.VITE_API_TOKEN}`
-          }
+          {
+            Authorization: `Bearer ${robotSettingState.selectedModel.apiKey}`
+          }

Add a guard at the start of sendStreamRequest:

 const sendStreamRequest = async () => {
+  if (!robotSettingState.selectedModel?.apiKey) {
+    requestLoading.value = false
+    inProcesing.value = false
+    Notify({ type: 'error', message: '未配置 API Key', position: 'top-right', duration: 5000 })
+    return
+  }
   const requestData = getSendSeesionProcess()
#!/bin/bash
# Ensure no remaining fallbacks to VITE_API_TOKEN
rg -n -C2 'VITE_API_TOKEN' --type js --type ts --type vue

Also applies to: 468-469

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/plugins/robot/src/Main.vue (1)

223-236: Do not persist API keys in localStorage.

setContextSession stores selectedModel including apiKey into localStorage. This is a client-side secret leak.

Apply:

-      localStorage.setItem(
-        'aiChat',
-        sessionProcess
-          ? JSON.stringify(sessionProcess)
-          : JSON.stringify({
-              foundationModel: {
-                ...robotSettingState.selectedModel
-              },
-              messages: [],
-              displayMessages: [], // 专门用来进行展示的消息,非原始消息,仅作为展示但是不作为请求的发送
-              aiType: aiType.value
-            })
-      )
+      const { apiKey, ...safeModel } = robotSettingState.selectedModel
+      localStorage.setItem(
+        'aiChat',
+        sessionProcess
+          ? JSON.stringify(sessionProcess)
+          : JSON.stringify({
+              foundationModel: safeModel,
+              messages: [],
+              displayMessages: [],
+              aiType: aiType.value
+            })
+      )
packages/plugins/robot/src/RobotSettingPopover.vue (1)

159-166: Fix potential crash when baseUrl has no matching option.

Indexing state.modelOptions[0] without ensuring the array exists will throw.

Apply:

-      const options = AIModelOptions.find((option) => option.value === state.existFormData.baseUrl)
-      state.modelOptions = options?.model
-      state.existFormData.label = options?.label
-      state.existFormData.model = state.modelOptions[0]?.value || ''
-      state.existFormData.completeModel = state.modelOptions[0]?.value || ''
+      const options = AIModelOptions.find((o) => o.value === state.existFormData.baseUrl)
+      state.modelOptions = options?.model ?? []
+      state.existFormData.label = options?.label ?? ''
+      state.existFormData.model = state.modelOptions?.[0]?.value ?? ''
+      state.existFormData.completeModel = state.modelOptions?.[0]?.value ?? ''
🧹 Nitpick comments (6)
packages/plugins/robot/src/js/useRobot.ts (3)

65-68: Cache or memoize getAIModelOptions result for stability.

Multiple calls can return different references if options are mutated externally; also avoids repeated lookups.

Consider memoizing once per session or returning a frozen copy to discourage mutation.


117-130: Don’t throw in initBlockList; propagate errors via return and let caller decide.

Throwing here bubbles into onMounted in Main.vue and can prevent cleanup/further init.

Either handle here or ensure the caller wraps with try/finally. See Main.vue comment proposing a caller-side fix.


18-22: Prefer ability-driven checks over VISUAL_MODEL list.

You already annotate visual-capable models via ability: ['visual']. Keeping VISUAL_MODEL risks drift.

Remove VISUAL_MODEL from the public surface and rely on ability when determining visual features.

packages/plugins/robot/src/Main.vue (2)

598-601: Ensure loader closes and chat initializes even if initBlockList fails.

Wrap with try/catch/finally.

Apply:

-      await initBlockList()
-      loadingInstance.close()
-      initChat()
+      try {
+        await initBlockList()
+      } catch (err) {
+        // eslint-disable-next-line no-console
+        console.error('初始化区块失败', err)
+      } finally {
+        loadingInstance.close()
+      }
+      initChat()

511-516: Avoid hard-coded user name in messages.

Using 'John' is misleading; prefer current user or omit.

Apply:

-      return {
-        role: 'user',
-        content,
-        name: 'John'
-      }
+      return { role: 'user', content }
packages/plugins/robot/src/RobotSettingPopover.vue (1)

42-43: Mask API keys in inputs.

Use password input to avoid shoulder-surfing.

Apply:

-            <tiny-input class="filedName" v-model="state.existFormData.apiKey" placeholder="请输入"></tiny-input>
+            <tiny-input class="filedName" type="password" v-model="state.existFormData.apiKey" placeholder="请输入"></tiny-input>
-            <tiny-input class="filedName" v-model="state.customizeFormData.apiKey" placeholder="请输入"></tiny-input>
+            <tiny-input class="filedName" type="password" v-model="state.customizeFormData.apiKey" placeholder="请输入"></tiny-input>

Also applies to: 79-80

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de0969a and 1c6e2cb.

📒 Files selected for processing (4)
  • packages/plugins/robot/src/Main.vue (22 hunks)
  • packages/plugins/robot/src/RobotSettingPopover.vue (10 hunks)
  • packages/plugins/robot/src/RobotTypeSelect.vue (5 hunks)
  • packages/plugins/robot/src/js/useRobot.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-01-14T06:49:00.797Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1011
File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:63-73
Timestamp: 2025-01-14T06:49:00.797Z
Learning: In the tiny-engine project, the SvgIcon component is globally registered and available throughout Vue components without requiring explicit imports.

Applied to files:

  • packages/plugins/robot/src/Main.vue
📚 Learning: 2025-01-14T06:55:59.692Z
Learnt from: gene9831
PR: opentiny/tiny-engine#1011
File: packages/configurator/src/router-select-configurator/RouterSelectConfigurator.vue:95-98
Timestamp: 2025-01-14T06:55:59.692Z
Learning: The tiny-select component from opentiny/vue library ensures selected options are valid internally, requiring no additional validation in the change handler.

Applied to files:

  • packages/plugins/robot/src/RobotSettingPopover.vue
🧬 Code graph analysis (1)
packages/plugins/robot/src/js/useRobot.ts (1)
packages/register/src/common.ts (1)
  • getOptions (32-34)
⏰ 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: push-check
🔇 Additional comments (5)
packages/plugins/robot/src/RobotTypeSelect.vue (1)

48-67: LGTM — moved to AI_MODES from hook and simplified defaults.

Bindings and emits look correct.

packages/plugins/robot/src/Main.vue (3)

617-646: Good fix on model-switch comparison.

Comparing selectedModel.model to model.model prevents unintended session clearing.


357-367: Remove client-side fallback to VITE_API_TOKEN; require explicit apiKey or use a secure server token.

Reintroduces a previously flagged security risk by falling back to import.meta.env.VITE_API_TOKEN.

Apply:

-          await sendMcpRequest(messages.value, {
-            model: robotSettingState.selectedModel.model,
-            headers: {
-              Authorization: `Bearer ${robotSettingState.selectedModel.apiKey || import.meta.env.VITE_API_TOKEN}`
-            },
-            baseUrl: robotSettingState.selectedModel.baseUrl
-          })
+          const token = robotSettingState.selectedModel.apiKey?.trim()
+          if (!token) {
+            Notify({ type: 'error', message: '缺少 API Key,请在设置中配置后重试。', position: 'top-right', duration: 5000 })
+            return
+          }
+          await sendMcpRequest(messages.value, {
+            model: robotSettingState.selectedModel.model,
+            headers: { Authorization: `Bearer ${token}` },
+            baseUrl: robotSettingState.selectedModel.baseUrl
+          })

462-463: Same: remove fallback to VITE_API_TOKEN in chatStream headers.

Apply:

-          {
-            Authorization: `Bearer ${robotSettingState.selectedModel.apiKey || import.meta.env.VITE_API_TOKEN}`
-          }
+          {
+            Authorization: `Bearer ${robotSettingState.selectedModel.apiKey}`
+          }

And guard before invoking chatStream:

+        if (!robotSettingState.selectedModel.apiKey?.trim()) {
+          Notify({ type: 'error', message: '缺少 API Key,请在设置中配置后重试。', position: 'top-right', duration: 5000 })
+          inProcesing.value = false
+          return
+        }
packages/plugins/robot/src/RobotSettingPopover.vue (1)

185-191: Confirm necessity of completeModel; remove if unused.
Automated search failed (ripgrep skipped files), so verification is inconclusive. Check whether Main.vue or any other code reads the 'AICompleteModel' localStorage key; if not, remove the persisted completeModel to avoid redundant state.
Location: packages/plugins/robot/src/RobotSettingPopover.vue (lines 185–191).

@hexqi hexqi merged commit 1109de0 into opentiny:develop Sep 23, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants