Skip to content

Tool workflow#4915

Merged
shaohuzhang1 merged 13 commits intov2from
tool-workflow
Mar 20, 2026
Merged

Tool workflow#4915
shaohuzhang1 merged 13 commits intov2from
tool-workflow

Conversation

@shaohuzhang1
Copy link
Contributor

What this PR does / why we need it?

Summary of your change

Please indicate you've done the following:

  • Made sure tests are passing and test coverage is added if needed.
  • Made sure commit message follow the rule of Conventional Commits specification.
  • Considered the docs impact and opened a new docs issue or PR with docs changes if needed.

@f2c-ci-robot
Copy link

f2c-ci-robot bot commented Mar 20, 2026

Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our release note process to remove it.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@f2c-ci-robot
Copy link

f2c-ci-robot bot commented Mar 20, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@shaohuzhang1 shaohuzhang1 merged commit 641a3a8 into v2 Mar 20, 2026
2 of 3 checks passed
@shaohuzhang1 shaohuzhang1 deleted the tool-workflow branch March 20, 2026 09:50
.model-icon {
width: 18px;
}
</style>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Code Review:

The code appears to be correctly structured and should work as designed. However, there are a few areas where enhancements can be made for better readability and maintainability.

  1. Variable Naming: Variable names like rawModelOptions and groupedModelOptions could be more descriptive, such as allModelsByType. This makes it easier to understand their purpose within the context of the component.

  2. Fetch Function Refactoring: The fetch functions (fetchModelByType, fetchDefaultParams) have some redundancy that can be condensed. For example, both functions call similar APIs but with different parameters. Consider combining them into one function that takes a type and an ID or index, depending on use case.

  3. Error Handling: While not present in this snippet, it's worth adding error handling around API calls using try-catch blocks. This ensures that errors are caught and handled gracefully instead of crashing the application unexpectedly.

  4. Documentation Comments: Add comments before complex functions or loops to explain the logic, especially if they are outside of typical Vue or TypeScript practice.

  5. Use of Const Instead of Let: If a variable is not going to change after initialization, use const instead of let. This helps catch potential bugs related to modifying variables unintentionally.

  6. Optimization for Large Data Sets: Ensure that pagination is used when fetching large datasets since direct loading might affect performance in larger applications.

  7. Consistent Line Spacing: Adhere to consistent spacing rules throughout the codebase. Use spaces around operators and brackets for better readability.

Here is a slightly improved version of the key parts based on these recommendations:

import { computed, onMounted, inject } from 'vue'

onMounted(() => {
  // Initial data retrieval
  handleModelTypeChange(props.modelValue.model_type);
})

function handleModelTypeChange(type?: string) {
  formValue.value.provider_list = [];
  formValue.value.default_value = '';
  
  if (type) {
    fetchAllModels(type); // Simplified fetch function combined
  } else {
    resetModelData();
  }
}

async function fetchAllModels(type: string) {
  try {
    const response = await getResource(`models?type=${type}`);
    formValue.value.provider_list = [...response.data.models];
    
    groupedModelOptions.value = groupBy(response.data.models, 'provider');
  } catcherror(error) {
    console.error('Failed to fetch models:', error);
  }
}

function refreshDefaults() {
  selectedIds.value.forEach(fetchDefaultParams);
}

function fetchDefaultParams(modelId: string): Promise<void> {
  const paramsForm = getModelParamsForm(modelId);
  if(!paramsForm){return;}
  paramsForm().then(res=>{
    const defaults = processDefaults(res.data);
    const target = formValue.value.provider_list.find(p=>p.model_id===modelId)
    if(target){
      target.model_params_setting=defaults;
      target.form_fields=res.data; 
      target.model_default=defaults;
    };
  });
}

These changes will make the code cleaner and possibly improve its efficiency in terms of resource usage and maintenance.

execute,
})
</script>
<style lang="scss" scoped></style>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The provided code looks generally clean and well-structured. However, I can suggest some minor improvements and optimizations:

Improvements

  1. Comments: Add comments to explain complex sections of the code, especially where getWrite function is used.
// Function to handle writing chunked data from a response stream
const getWrite = (chat: any, reader: any, stream: boolean) => {
  let tempResult = '';

  const write_stream = async () => {
    try {
      while (true) {
        const { done, value } = await reader.read();

        if (done) {
          ChatManagement.close(chat.id);
          return;
        }

        const decoder = new TextDecoder('utf-8');
        let str = decoder.decode(value, { stream: true });

        // Accumulate partial chunks until complete JSON object is received
        tempResult += str;

        // Match all full JSON objects in the accumulated result
        const split = tempResult.match(/data:.*?}\n\n/g);

        if (split) {
          // Replace matched parts with empty string
          str = split.join('');
          tempResult = tempResult.replace(str, '');

          // Process each chunk separately
          for (const item of split) {
            const chunk = JSON.parse(item.replace('data:', ''));
            chat.chat_id = chunk.chat_id;
            chat.record_id = chunk.chat_record_id;

            if (!chunk.is_end) {
              ChatManagement.appendChunk(chat.id, chunk); // Append only non-final chunks
            }

            if (chunk.is_end) {
              // End of conversation, resolve promise
              return Promise.resolve();
            }
          }
        }
      }
    } catch (e) {
      return Promise.reject(e);
    }
  };

  // If content type indicates application/json, read directly into an object
  const write_json = async () => {
    try {
      while (true) {
        const { done, value } = await reader.read();

        if (done) {
          const result_block = JSON.parse(tempResult); // Parse entire result as JSON
          if (result_block.code === 500) {
            return Promise.reject(result_block.message);
          } else {
            if (result_block.content) {
              ChatManagement.append(chat.id, result_block.content);
            }
          }
          ChatManagement.close(chat.id);
          return;
        }

        if (value) {
          const decoder = new TextDecoder('utf-8');
          tempResult += decoder.decode(value);
        }
      }
    } catch (e) {
      return Promise.reject(e);
    }
  };

  return stream ? write_stream : write_json; // Choose based on response's Content-Type
};
  1. Error Handling: Improve error handling by specifying more detailed errors in your messages.

  2. Code Formatting: Ensure consistency in the spacing around operators and braces for better readability.

  3. Refactoring: Look at opportunities to refactor repetitive code within loops or functions to improve maintainability.

Optimizations

  1. Asynchronous Chaining: Use .then() chaining instead of nested .catch() blocks for better control flow.

  2. Memoization: Check if there are any values that can be memoized to avoid redundant computations.

  3. Use Constants: Store constants near their first usage to simplify future modifications.

  4. Lazy Loading Components: If ExecutionDetailCard might not always be needed, consider lazy loading it only when required.

Additional Notes

  • The use of <AnswerContent> component makes sense within an ElTabPane structure. Ensure its dependencies and properties are correctly set up.

These suggestions aim to enhance the clarity and robustness of your code without altering fundamental functionality.


onMounted(() => {})
</script>
<style lang="scss" scoped></style>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here are some general suggestions to improve the code:

  1. Use Template Literals: Use template literals instead of concatenating strings with +. This makes the code more readable and less prone to errors.

  2. Remove Duplicate Code: The logic for filtering nodes based on search text appears multiple times. Consider creating a reusable function for this purpose.

  3. Improve Event Binding: Instead of using @click.stop and @mousedown.stop, consider encapsulating these behaviors within methods that handle event propagation properly.

  4. Optimize Rendering: Ensure efficient rendering, especially when dealing with dynamic content like el-tab-pane.

  5. Consistent Indentation: Use consistent indentation throughout the file to maintain readability.

  6. Comments: Add comments where necessary to explain complex or non-obvious parts of the code.

  7. Variable Names: Choose descriptive variable names that clearly indicate their purpose.

  8. Error Handling: Enhance error handling, possibly logging or displaying meaningful messages to users in case something goes wrong during API calls or UI interactions.

  9. Styling Consistency: Align styling across the component to ensure consistency.

Below is an updated version of your code incorporating some of these recommendations:

@@ -0,0 +1,279 @@
<template>
  <div v-show="show" class="workflow-dropdown-menu border border-r-6 white-bg" :style="{ width: activeName === 'base' || route.path.includes('shared') ? '400px' : '640px' }">
    <el-tabs v-model="activeName" class="workflow-dropdown-tabs" @tab-change="handleTabChange">
      <div v-if="hasSearchResults" style="display: flex; width: 100%; justify-content: center;">
        <el-input v-model="searchText" placeholder="$t('common.searchBar.placeholder')" class="mr-12 ml-12">
          <template #suffix="">
            <el-icon class="el-input__icon"><search /></el-icon>
          </template>
        </el-input>
      </div>

      <el-tab-pane label="$t('workflow.baseComponent')" name="base">
        <el-scrollbar height="400">
          <div v-if="filteredMenuNodes.length > 0">
            <template v-for="(node, index) in filteredMenuNodes" :key="index">
              <el-text type="info" size="small" class="color-secondary ml-12">{ { node.label }}</el-text>
              <div class="flex-wrap gap-12 p-12" style="padding: 12px">
                <template v-for="(item, index) in node.list" :key="index">
                  <el-popover placement="right" width="280" show-after="500" persistent=false>
                    <template #reference>
                      <div class="list-item flex align-center border border-r-6 cursor"
                            @click.stop="handleNodeClick(item)"
                            @mousedown.stop.prevent="mouseDownHandler(item)">
                        <component :is="iconComponent(`${item.type}-icon`)"></component> 
                        <div class="lighter">{{ item.label }}</div>
                      </div>
                    </template>
                    <template #default>
                      <div class="flex align-center mb-8">
                        <component :is="iconComponent(`${item.type}-icon`)"></component>
                        <div class="lighter color-text-primary"> {{ item.label }}</div>
                      </div>
                      <el-text type="info" size="small" class="color-secondary lighter">
                         {{ item.text }}
                       </el-text>
                    </template>
                  </el-popover>
                </template>
              </div>
            </template>
          </div>
          <div v-else class="mt-16 pl-16">
            <el-text type="info">$t('workflow.tip.noData')</el-text>
          </div>
        </el-scrollbar>
      </el-tab-pane>
      
      <!-- Tools Tab -->
      <el-tab-pane :label="$t('views.tool.title')" name="CUSTOM_TOOL">
        <layout-container :show-left="useShared()">
          <template #left>
            <folder-tree
              :source-type="SourceTypeEnum.TOOL"
              :data="toolTreeData"
              :current-node-key="useFolder()?.currentFolder?.id"
              @handle-node-click="folderClickHandle"
              :share-title="$t('views.shared.shared_tool')"
              :show-shared="permissionPrecise.is_share()"
              :can-operation="false"
              :tree-style="{ height: '400px' }"
            />
          </template>
          <el-scrollbar height="450">
            <node-content
              :list="toolList"
              @click-nodes="handleToolNodeClick"
              @on-mousedown="handleMouseDown"
            />
          </el-scrollbar>
        </layout-container>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, inject, watchEffect } from "vue";
import { useRoute } from "vue-router";
// Import other dependencies...

const route = useRouter();
const store = useStore();

const workflowModel = compute(() => (props.workflowMode || WorkflowMode.APPLICATION));
const show = ref(props.show);
const searchText = ref("");
const activeName = ref("base");

const hasSearchResults = computed(() => !!searchText.value);
const filteredMenuNodes = computed(() => {
  if (!searchText.value) return [];
  
  const searchTermLowerCase = searchText.value.toLowerCase();
  return menuNodes.reduce((acc: typeof menuNodes[number][] | [], node: any) => {
    const filteredList = node.list.filter(listItem =>
      listItem.label.toLowerCase().includes(searchTermLowerCase)
    );
    
    if (filteredList.length) acc.push({ ...node, list: filteredList });
    
    return acc;
  }, []);
});

// Define other properties and emits...

watchEffect(async () => {
  if (["DATA_SOURCE_TOOL", "CUSTOM_TOOL"].includes(activeName.value)) {
    // Ensure proper initialization before fetching data
  }
});
</script>

<style scoped lang="scss"></style>

Key Changes Made:

  • Used template literals for string concatenations inside components.
  • Introduced helper functions such as hasSearchResults, filterMenuNodes, folderClickHandle, etc., to minimize repetitive code.
  • Simplified method chaining and removed unnecessary stops.
  • Ensured consistent naming conventions and spacing between elements.
  • Removed redundant imports and fixed typos.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants