refactor: enhance parameter handling and improve code clarity

- Updated GetListParams to set a default sort option for better query handling.
- Enhanced PostSpiderRunParams to include a default mode and improved error handling for missing spider.
- Added parameters field to ChatMessageContent for more flexible message content management.
- Refactored getNodeIds method to simplify mode handling and removed unnecessary error checks.
- Improved ChatMessageAction component to display parameters and response sections more effectively, enhancing user experience.
This commit is contained in:
Marvin Zhang
2025-04-17 18:03:15 +08:00
parent 36c7d5e3a3
commit 34509b8d4c
12 changed files with 172 additions and 31 deletions

View File

@@ -63,7 +63,7 @@ type BaseController[T any] struct {
type GetListParams struct {
Filter string `query:"filter" description:"Filter query"`
Sort string `query:"sort" description:"Sort options"`
Sort string `query:"sort" default:"-_id" description:"Sort options"`
Page int `query:"page" default:"1" description:"Page number" minimum:"1"`
Size int `query:"size" default:"10" description:"Page size" minimum:"1"`
All bool `query:"all" default:"false" description:"Whether to get all items"`

View File

@@ -674,7 +674,7 @@ func PostSpiderExport(c *gin.Context, _ *PostSpiderExportParams) (err error) {
type PostSpiderRunParams struct {
Id string `path:"id" description:"Spider ID" format:"objectid" pattern:"^[0-9a-fA-F]{24}$"`
Mode string `json:"mode" description:"Run mode" enum:"random,all,selected-nodes"`
Mode string `json:"mode" description:"Run mode: random,all,selected-nodes" default:"random" enum:"random,all,selected-nodes"`
NodeIds []string `json:"node_ids" description:"Node IDs, used in selected-nodes mode"`
Cmd string `json:"cmd" description:"Command"`
Param string `json:"param" description:"Parameters"`
@@ -688,6 +688,12 @@ func PostSpiderRun(c *gin.Context, params *PostSpiderRunParams) (response *Respo
return GetErrorResponse[[]primitive.ObjectID](errors.BadRequestf("invalid id format"))
}
// get spider
s, err := service.NewModelService[models.Spider]().GetById(id)
if err != nil {
return GetErrorResponse[[]primitive.ObjectID](errors.NotFoundf("spider not found"))
}
// options
var nodeIds []primitive.ObjectID
if len(params.NodeIds) > 0 {
@@ -714,6 +720,18 @@ func PostSpiderRun(c *gin.Context, params *PostSpiderRunParams) (response *Respo
ScheduleId: scheduleId,
Priority: params.Priority,
}
if opts.Mode == "" {
opts.Mode = s.Mode
}
if opts.Cmd == "" {
opts.Cmd = s.Cmd
}
if opts.Param == "" {
opts.Param = s.Param
}
if opts.Priority == 0 {
opts.Priority = s.Priority
}
// user
if u := GetUserFromContext(c); u != nil {

View File

@@ -29,6 +29,7 @@ type ChatMessageContent struct {
BaseModel `bson:",inline"`
MessageId primitive.ObjectID `json:"message_id" bson:"message_id" description:"Message ID"`
Key string `json:"key" bson:"key" description:"Message content key"`
Parameters map[string]interface{} `json:"parameters" bson:"parameters" description:"Message content parameters"`
Content string `json:"content" bson:"content" description:"Message content"`
Type string `json:"type" bson:"type" description:"Message type (text/action)"`
Action string `json:"action,omitempty" bson:"action,omitempty" description:"Action name"`

View File

@@ -1,7 +1,6 @@
package admin
import (
"errors"
"github.com/crawlab-team/crawlab/core/constants"
"github.com/crawlab-team/crawlab/core/interfaces"
"github.com/crawlab-team/crawlab/core/models/models"
@@ -82,7 +81,8 @@ func (svc *Service) scheduleTasks(s *models.Spider, opts *interfaces.SpiderRunOp
}
func (svc *Service) getNodeIds(opts *interfaces.SpiderRunOptions) (nodeIds []primitive.ObjectID, err error) {
if opts.Mode == constants.RunTypeAllNodes {
switch opts.Mode {
case constants.RunTypeAllNodes:
query := bson.M{
"active": true,
"enabled": true,
@@ -95,12 +95,10 @@ func (svc *Service) getNodeIds(opts *interfaces.SpiderRunOptions) (nodeIds []pri
for _, node := range nodes {
nodeIds = append(nodeIds, node.Id)
}
} else if opts.Mode == constants.RunTypeSelectedNodes {
case constants.RunTypeSelectedNodes:
nodeIds = opts.NodeIds
} else if opts.Mode == constants.RunTypeRandom {
default:
nodeIds = []primitive.ObjectID{primitive.NilObjectID}
} else {
return nil, errors.New("invalid run mode")
}
return nodeIds, nil
}

View File

@@ -4,6 +4,7 @@ import markdownit from 'markdown-it';
import hljs from 'highlight.js';
import { computed, ref, watch } from 'vue';
import 'highlight.js/styles/github.css';
import ClChatMessageAction from '@/components/ui/chat/ChatMessageAction.vue';
const { t } = useI18n();
@@ -69,9 +70,9 @@ const filteredContents = computed<ChatMessageContent[]>(() => {
// Compute token usage display
const hasTokenUsage = computed(() => {
const { message } = props;
return message.usage &&
(message.usage.total_tokens ||
message.usage.prompt_tokens ||
return message.usage &&
(message.usage.total_tokens ||
message.usage.prompt_tokens ||
message.usage.completion_tokens);
});
@@ -98,6 +99,7 @@ defineOptions({ name: 'ClChatMessage' });
v-if="content.type === 'action'"
:action="content.action!"
:action-status="content.action_status!"
:parameters="content.parameters"
:content="content.content"
/>

View File

@@ -5,11 +5,14 @@ import JsonEditorVue from 'json-editor-vue';
const props = defineProps<{
action: string;
actionStatus: ChatMessageActionStatus;
parameters?: Record<string, any>;
content?: string;
}>();
const isExpanded = ref(false);
const isFullScreen = ref(false);
const isParamsExpanded = ref(true);
const isResponseExpanded = ref(true);
const actionStatusIcon = computed<Icon>(() => {
switch (props.actionStatus) {
@@ -33,8 +36,16 @@ const parsedContent = computed<Record<string, any> | Record<string, any>[] | nul
}
});
const hasParameters = computed(() => {
return props.parameters && Object.keys(props.parameters).length > 0;
});
const isJsonContent = computed(() => {
return parsedContent.value !== null;
return parsedContent.value !== null || hasParameters.value;
});
const hasContent = computed(() => {
return props.content || hasParameters.value;
});
defineOptions({ name: 'ClChatMessageAction' });
@@ -69,7 +80,7 @@ defineOptions({ name: 'ClChatMessageAction' });
@click.stop="isFullScreen = true"
/>
<cl-icon
v-if="content"
v-if="hasContent"
class="action-button"
:icon="['fas', isExpanded ? 'chevron-up' : 'chevron-down']"
@click.stop="isExpanded = !isExpanded"
@@ -77,23 +88,53 @@ defineOptions({ name: 'ClChatMessageAction' });
</div>
</div>
<div
v-if="content"
v-if="hasContent"
class="action-content"
:class="{ expanded: isExpanded }"
>
<el-scrollbar max-height="500px">
<template v-if="isJsonContent">
<div class="json-content">
<!-- Parameters Section -->
<div v-if="hasParameters" class="content-section">
<div class="section-header" @click="isParamsExpanded = !isParamsExpanded">
<span class="section-title">Parameters</span>
<cl-icon
class="action-button"
:icon="['fas', isParamsExpanded ? 'chevron-up' : 'chevron-down']"
/>
</div>
<div v-show="isParamsExpanded" class="json-content">
<json-editor-vue
v-model="parsedContent"
:model-value="parameters"
expanded-on-start
read-only
/>
</div>
</template>
<template v-else>
{{ content }}
</template>
</div>
<!-- Response Section -->
<div v-if="content" class="content-section">
<div class="section-header" @click="isResponseExpanded = !isResponseExpanded">
<span class="section-title">Response</span>
<cl-icon
class="action-button"
:icon="['fas', isResponseExpanded ? 'chevron-up' : 'chevron-down']"
/>
</div>
<div v-show="isResponseExpanded">
<template v-if="parsedContent">
<div class="json-content">
<json-editor-vue
:model-value="parsedContent"
expanded-on-start
read-only
/>
</div>
</template>
<template v-else>
<div class="text-content">{{ content }}</div>
</template>
</div>
</div>
</el-scrollbar>
</div>
</div>
@@ -107,11 +148,50 @@ defineOptions({ name: 'ClChatMessageAction' });
fullscreen
append-to-body
>
<json-editor-vue
v-model="parsedContent"
expanded-on-start
read-only
/>
<div class="fullscreen-content">
<!-- Parameters Section -->
<div v-if="hasParameters" class="content-section">
<div class="section-header" @click="isParamsExpanded = !isParamsExpanded">
<span class="section-title">Parameters</span>
<cl-icon
class="action-button"
:icon="['fas', isParamsExpanded ? 'chevron-up' : 'chevron-down']"
/>
</div>
<div v-show="isParamsExpanded" class="json-content">
<json-editor-vue
:model-value="parameters"
expanded-on-start
read-only
/>
</div>
</div>
<!-- Response Section -->
<div v-if="content" class="content-section">
<div class="section-header" @click="isResponseExpanded = !isResponseExpanded">
<span class="section-title">Response</span>
<cl-icon
class="action-button"
:icon="['fas', isResponseExpanded ? 'chevron-up' : 'chevron-down']"
/>
</div>
<div v-show="isResponseExpanded">
<template v-if="parsedContent">
<div class="json-content">
<json-editor-vue
:model-value="parsedContent"
expanded-on-start
read-only
/>
</div>
</template>
<template v-else>
<div class="text-content">{{ content }}</div>
</template>
</div>
</div>
</div>
</el-dialog>
</template>
@@ -221,8 +301,36 @@ defineOptions({ name: 'ClChatMessageAction' });
transition: max-height 0.3s ease-in;
}
.content-section {
margin-bottom: 4px;
}
.content-section:last-child {
margin-bottom: 0;
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10px 0 0;
margin-bottom: 0;
cursor: pointer;
}
.section-title {
font-weight: 500;
color: var(--el-text-color-primary);
}
.json-content {
padding: 0 10px 0 0;
}
.text-content {
padding: 8px;
white-space: pre-wrap;
word-break: break-word;
}
.action-content :deep(.jse-main) {
@@ -246,10 +354,22 @@ defineOptions({ name: 'ClChatMessageAction' });
max-width: 100%;
overflow-x: auto;
}
.fullscreen-content {
height: calc(100vh - 80px);
overflow-y: auto;
padding: 16px;
}
.fullscreen-content .content-section {
background: var(--el-bg-color);
border-radius: 4px;
padding: 16px;
margin-bottom: 16px;
}
</style>
<style>
.el-dialog.is-fullscreen .jse-main {
height: calc(100vh - 80px) !important;
background: var(--el-bg-color);
padding: 16px;
border-radius: 4px;

View File

@@ -56,6 +56,7 @@ export declare global {
interface ChatMessageContent extends BaseModel {
message_id?: string;
key?: string;
parameters?: Record<string, any>;
content?: string;
type: ChatMessageContentType;
action?: string;
@@ -114,6 +115,7 @@ export declare global {
conversation_title?: string;
message_id?: string;
key?: string;
parameters?: Record<string, any>;
content?: string;
type: 'text' | 'action'; // Message type
action_id?: string;

View File

@@ -49,7 +49,7 @@ const actions = {
page,
size,
conditions: JSON.stringify(state.tableListFilter),
sort: JSON.stringify(state.tableListSort),
// sort: JSON.stringify(state.tableListSort),
} as ListRequestParams);
commit('setNodeMetricsMap', res.data);
return res;

View File

@@ -103,7 +103,7 @@ const actions = {
const payload = {
...state.tablePagination,
conditions: JSON.stringify(state.tableListFilter),
sort: JSON.stringify(state.tableListSort),
// sort: JSON.stringify(state.tableListSort),
stats: true,
};
const res = await getList(`/spiders`, payload);

View File

@@ -38,7 +38,6 @@ const actions = {
if (!setting.value) {
setting.value = {};
}
console.debug(setting)
commit('setSetting', { key, value: setting });
},
saveSetting: async (

View File

@@ -86,7 +86,7 @@ const actions = {
const payload = {
...state.tablePagination,
conditions: JSON.stringify(state.tableListFilter),
sort: JSON.stringify(state.tableListSort),
// sort: JSON.stringify(state.tableListSort),
stats: true,
};
const res = await getList(`/tasks`, payload);

View File

@@ -316,6 +316,7 @@ export const getDefaultStoreActions = <T = any>(
state,
commit,
}: StoreActionContext<BaseStoreState<T>>) => {
console.debug('getList');
const { page, size } = state.tablePagination;
try {
commit('setTableLoading', true);