feat(cockpit): product-grade LLM integration with context + model mgmt
Backend: - cockpit_chat.dspy: real LLM calls via llm table config, Hermes-style context (system prompt + project/iteration + history), auto model selection (multimodal if files, text otherwise) - cockpit_model_options.dspy: returns active models with capabilities - llm table: added capabilities column (text/vision/multimodal) Frontend: - inputed script: no page reload, updates agent bubble with LLM reply - stats_row refreshed on success, error shown in bubble DB: - sd_agent_settings table for per-user agent preferences
This commit is contained in:
parent
d7bace21db
commit
72cd262cdb
@ -1,70 +1,103 @@
|
||||
{
|
||||
"tblname": "sd_bugs",
|
||||
"title": "Bug管理",
|
||||
"params": {
|
||||
"sortby": ["created_at desc"],
|
||||
"confidential_fields": [],
|
||||
"browserfields": {
|
||||
"alters": {
|
||||
"iteration_id": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_iteration_options.dspy')}}",
|
||||
"valueField": "iteration_id",
|
||||
"textField": "iteration_id_text"
|
||||
},
|
||||
"severity": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_severity.dspy')}}",
|
||||
"valueField": "severity",
|
||||
"textField": "severity_text"
|
||||
},
|
||||
"priority": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_priority.dspy')}}",
|
||||
"valueField": "priority",
|
||||
"textField": "priority_text"
|
||||
},
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_bug_status.dspy')}}",
|
||||
"valueField": "status",
|
||||
"textField": "status_text"
|
||||
},
|
||||
"reporter_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_reporter_type.dspy')}}",
|
||||
"valueField": "reporter_type",
|
||||
"textField": "reporter_type_text"
|
||||
}
|
||||
}
|
||||
"tblname": "sd_bugs",
|
||||
"title": "Bug管理",
|
||||
"params": {
|
||||
"sortby": [
|
||||
"created_at desc"
|
||||
],
|
||||
"confidential_fields": [],
|
||||
"browserfields": {
|
||||
"alters": {
|
||||
"iteration_id": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_iteration_options.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"editexclouded": ["id", "created_at", "updated_at", "closed_at", "verified_by", "fix_commit"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_bug_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_bug_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_bug_delete.dspy')}}"
|
||||
"severity": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_severity.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"data_filter": {
|
||||
"AND": [
|
||||
{"field": "iteration_id", "op": "=", "var": "iteration_input"},
|
||||
{"field": "severity", "op": "=", "var": "severity_input"},
|
||||
{"field": "status", "op": "=", "var": "status_input"},
|
||||
{"field": "title", "op": "LIKE", "var": "title_input"}
|
||||
]
|
||||
"priority": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_priority.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"record_toolbar": [
|
||||
{
|
||||
"label": "确认",
|
||||
"actiontype": "dspy",
|
||||
"url": "{{entire_url('../api/bug_confirm.dspy')}}",
|
||||
"options": {"icon": "check", "cwidth": 16, "cheight": 9}
|
||||
},
|
||||
{
|
||||
"label": "关闭",
|
||||
"actiontype": "dspy",
|
||||
"url": "{{entire_url('../api/bug_close.dspy')}}",
|
||||
"options": {"icon": "block", "cwidth": 16, "cheight": 9}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_bug_status.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"reporter_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_reporter_type.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"closed_at",
|
||||
"verified_by",
|
||||
"fix_commit"
|
||||
],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_bug_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_bug_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_bug_delete.dspy')}}"
|
||||
},
|
||||
"data_filter": {
|
||||
"AND": [
|
||||
{
|
||||
"field": "iteration_id",
|
||||
"op": "=",
|
||||
"var": "iteration_input"
|
||||
},
|
||||
{
|
||||
"field": "severity",
|
||||
"op": "=",
|
||||
"var": "severity_input"
|
||||
},
|
||||
{
|
||||
"field": "status",
|
||||
"op": "=",
|
||||
"var": "status_input"
|
||||
},
|
||||
{
|
||||
"field": "title",
|
||||
"op": "LIKE",
|
||||
"var": "title_input"
|
||||
}
|
||||
]
|
||||
},
|
||||
"record_toolbar": [
|
||||
{
|
||||
"label": "确认",
|
||||
"actiontype": "dspy",
|
||||
"url": "{{entire_url('../api/bug_confirm.dspy')}}",
|
||||
"options": {
|
||||
"icon": "check",
|
||||
"cwidth": 16,
|
||||
"cheight": 9
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "关闭",
|
||||
"actiontype": "dspy",
|
||||
"url": "{{entire_url('../api/bug_close.dspy')}}",
|
||||
"options": {
|
||||
"icon": "block",
|
||||
"cwidth": 16,
|
||||
"cheight": 9
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,45 +1,62 @@
|
||||
{
|
||||
"tblname": "sd_deploy_envs",
|
||||
"title": "部署环境",
|
||||
"params": {
|
||||
"sortby": ["env_type"],
|
||||
"confidential_fields": ["db_password", "ssh_key_path"],
|
||||
"browserfields": {
|
||||
"exclouded": ["db_password", "ssh_key_path"],
|
||||
"alters": {
|
||||
"project_id": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_project_options.dspy')}}",
|
||||
"valueField": "project_id",
|
||||
"textField": "project_id_text"
|
||||
},
|
||||
"env_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_env_type.dspy')}}",
|
||||
"valueField": "env_type",
|
||||
"textField": "env_type_text"
|
||||
},
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_env_status.dspy')}}",
|
||||
"valueField": "status",
|
||||
"textField": "status_text"
|
||||
}
|
||||
}
|
||||
"tblname": "sd_deploy_envs",
|
||||
"title": "部署环境",
|
||||
"params": {
|
||||
"sortby": [
|
||||
"env_type"
|
||||
],
|
||||
"confidential_fields": [
|
||||
"db_password",
|
||||
"ssh_key_path"
|
||||
],
|
||||
"browserfields": {
|
||||
"exclouded": [
|
||||
"db_password",
|
||||
"ssh_key_path"
|
||||
],
|
||||
"alters": {
|
||||
"project_id": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_project_options.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"editexclouded": ["id", "verified_at", "created_at", "updated_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_deploy_env_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_deploy_env_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_deploy_env_delete.dspy')}}"
|
||||
"env_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_env_type.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"record_toolbar": [
|
||||
{
|
||||
"label": "验证连接",
|
||||
"actiontype": "dspy",
|
||||
"url": "{{entire_url('../api/env_verify.dspy')}}",
|
||||
"options": {"icon": "check", "cwidth": 16, "cheight": 9}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_env_status.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id",
|
||||
"verified_at",
|
||||
"created_at",
|
||||
"updated_at"
|
||||
],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_deploy_env_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_deploy_env_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_deploy_env_delete.dspy')}}"
|
||||
},
|
||||
"record_toolbar": [
|
||||
{
|
||||
"label": "验证连接",
|
||||
"actiontype": "dspy",
|
||||
"url": "{{entire_url('../api/env_verify.dspy')}}",
|
||||
"options": {
|
||||
"icon": "check",
|
||||
"cwidth": 16,
|
||||
"cheight": 9
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,58 +1,81 @@
|
||||
{
|
||||
"tblname": "sd_iterations",
|
||||
"title": "迭代管理",
|
||||
"params": {
|
||||
"sortby": ["created_at desc"],
|
||||
"confidential_fields": [],
|
||||
"browserfields": {
|
||||
"exclouded": ["scope"],
|
||||
"alters": {
|
||||
"project_id": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_project_options.dspy')}}",
|
||||
"valueField": "project_id",
|
||||
"textField": "project_id_text"
|
||||
},
|
||||
"iteration_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_iteration_type.dspy')}}",
|
||||
"valueField": "iteration_type",
|
||||
"textField": "iteration_type_text"
|
||||
},
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_iteration_status.dspy')}}",
|
||||
"valueField": "status",
|
||||
"textField": "status_text"
|
||||
}
|
||||
}
|
||||
"tblname": "sd_iterations",
|
||||
"title": "迭代管理",
|
||||
"params": {
|
||||
"sortby": [
|
||||
"created_at desc"
|
||||
],
|
||||
"confidential_fields": [],
|
||||
"browserfields": {
|
||||
"exclouded": [
|
||||
"scope"
|
||||
],
|
||||
"alters": {
|
||||
"project_id": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_project_options.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"editexclouded": ["id", "task_id", "started_at", "completed_at", "created_at", "updated_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_iteration_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_iteration_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_iteration_delete.dspy')}}"
|
||||
"iteration_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_iteration_type.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"data_filter": {
|
||||
"AND": [
|
||||
{"field": "project_id", "op": "=", "var": "project_input"},
|
||||
{"field": "iteration_name", "op": "LIKE", "var": "name_input"},
|
||||
{"field": "status", "op": "=", "var": "status_input"}
|
||||
]
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_iteration_status.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id",
|
||||
"task_id",
|
||||
"started_at",
|
||||
"completed_at",
|
||||
"created_at",
|
||||
"updated_at"
|
||||
],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_iteration_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_iteration_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_iteration_delete.dspy')}}"
|
||||
},
|
||||
"data_filter": {
|
||||
"AND": [
|
||||
{
|
||||
"field": "project_id",
|
||||
"op": "=",
|
||||
"var": "project_input"
|
||||
},
|
||||
"subtables": [
|
||||
{
|
||||
"field": "iteration_id",
|
||||
"title": "Bug列表",
|
||||
"url": "{{entire_url('../sd_bug_list?iteration_id=${id}')}}",
|
||||
"subtable": "sd_bugs"
|
||||
},
|
||||
{
|
||||
"field": "iteration_id",
|
||||
"title": "测试方案",
|
||||
"url": "{{entire_url('../sd_test_plan_list?iteration_id=${id}')}}",
|
||||
"subtable": "sd_test_plans"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
{
|
||||
"field": "iteration_name",
|
||||
"op": "LIKE",
|
||||
"var": "name_input"
|
||||
},
|
||||
{
|
||||
"field": "status",
|
||||
"op": "=",
|
||||
"var": "status_input"
|
||||
}
|
||||
]
|
||||
},
|
||||
"subtables": [
|
||||
{
|
||||
"field": "iteration_id",
|
||||
"title": "Bug列表",
|
||||
"url": "{{entire_url('../sd_bug_list?iteration_id=${id}')}}",
|
||||
"subtable": "sd_bugs"
|
||||
},
|
||||
{
|
||||
"field": "iteration_id",
|
||||
"title": "测试方案",
|
||||
"url": "{{entire_url('../sd_test_plan_list?iteration_id=${id}')}}",
|
||||
"subtable": "sd_test_plans"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,47 +1,74 @@
|
||||
{
|
||||
"tblname": "sd_projects",
|
||||
"title": "项目管理",
|
||||
"params": {
|
||||
"sortby": ["created_at desc"],
|
||||
"logined_userorgid": "org_id",
|
||||
"confidential_fields": [],
|
||||
"browserfields": {
|
||||
"exclouded": ["tech_stack"],
|
||||
"alters": {
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_status.dspy')}}",
|
||||
"valueField": "status",
|
||||
"textField": "status_text"
|
||||
},
|
||||
"project_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_project_type.dspy')}}",
|
||||
"valueField": "project_type",
|
||||
"textField": "project_type_text"
|
||||
}
|
||||
}
|
||||
"tblname": "sd_projects",
|
||||
"title": "项目管理",
|
||||
"params": {
|
||||
"sortby": [
|
||||
"created_at desc"
|
||||
],
|
||||
"logined_userorgid": "org_id",
|
||||
"confidential_fields": [],
|
||||
"browserfields": {
|
||||
"exclouded": [
|
||||
"tech_stack"
|
||||
],
|
||||
"alters": {
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_status.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"editexclouded": ["id", "created_at", "updated_at", "org_id"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_project_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_project_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_project_delete.dspy')}}"
|
||||
"project_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_project_type.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"data_filter": {
|
||||
"AND": [
|
||||
{"field": "name", "op": "LIKE", "var": "name_input"},
|
||||
{"field": "status", "op": "=", "var": "status_input"},
|
||||
{"field": "project_type", "op": "=", "var": "type_input"}
|
||||
]
|
||||
"pipeline_id": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_pipeline_options.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"org_id"
|
||||
],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_project_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_project_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_project_delete.dspy')}}"
|
||||
},
|
||||
"data_filter": {
|
||||
"AND": [
|
||||
{
|
||||
"field": "name",
|
||||
"op": "LIKE",
|
||||
"var": "name_input"
|
||||
},
|
||||
"subtables": [
|
||||
{
|
||||
"field": "project_id",
|
||||
"title": "迭代列表",
|
||||
"url": "{{entire_url('../sd_iteration_list?project_id=${id}')}}",
|
||||
"subtable": "sd_iterations"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
{
|
||||
"field": "status",
|
||||
"op": "=",
|
||||
"var": "status_input"
|
||||
},
|
||||
{
|
||||
"field": "project_type",
|
||||
"op": "=",
|
||||
"var": "type_input"
|
||||
}
|
||||
]
|
||||
},
|
||||
"subtables": [
|
||||
{
|
||||
"field": "project_id",
|
||||
"title": "迭代列表",
|
||||
"url": "{{entire_url('../sd_iteration_list?project_id=${id}')}}",
|
||||
"subtable": "sd_iterations"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,50 +1,76 @@
|
||||
{
|
||||
"tblname": "sd_test_cases",
|
||||
"title": "测试用例",
|
||||
"params": {
|
||||
"sortby": ["priority desc", "created_at desc"],
|
||||
"confidential_fields": [],
|
||||
"browserfields": {
|
||||
"alters": {
|
||||
"plan_id": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_test_plan_options.dspy')}}",
|
||||
"valueField": "plan_id",
|
||||
"textField": "plan_id_text"
|
||||
},
|
||||
"case_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_case_type.dspy')}}",
|
||||
"valueField": "case_type",
|
||||
"textField": "case_type_text"
|
||||
},
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_case_status.dspy')}}",
|
||||
"valueField": "status",
|
||||
"textField": "status_text"
|
||||
},
|
||||
"priority": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_priority.dspy')}}",
|
||||
"valueField": "priority",
|
||||
"textField": "priority_text"
|
||||
}
|
||||
}
|
||||
"tblname": "sd_test_cases",
|
||||
"title": "测试用例",
|
||||
"params": {
|
||||
"sortby": [
|
||||
"priority desc",
|
||||
"created_at desc"
|
||||
],
|
||||
"confidential_fields": [],
|
||||
"browserfields": {
|
||||
"alters": {
|
||||
"plan_id": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_test_plan_options.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"editexclouded": ["id", "actual_result", "executed_by", "executed_at", "duration_ms", "created_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_test_case_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_test_case_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_test_case_delete.dspy')}}"
|
||||
"case_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_case_type.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"data_filter": {
|
||||
"AND": [
|
||||
{"field": "plan_id", "op": "=", "var": "plan_input"},
|
||||
{"field": "case_type", "op": "=", "var": "type_input"},
|
||||
{"field": "status", "op": "=", "var": "status_input"},
|
||||
{"field": "priority", "op": "=", "var": "priority_input"}
|
||||
]
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_case_status.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"priority": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_priority.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id",
|
||||
"actual_result",
|
||||
"executed_by",
|
||||
"executed_at",
|
||||
"duration_ms",
|
||||
"created_at"
|
||||
],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_test_case_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_test_case_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_test_case_delete.dspy')}}"
|
||||
},
|
||||
"data_filter": {
|
||||
"AND": [
|
||||
{
|
||||
"field": "plan_id",
|
||||
"op": "=",
|
||||
"var": "plan_input"
|
||||
},
|
||||
{
|
||||
"field": "case_type",
|
||||
"op": "=",
|
||||
"var": "type_input"
|
||||
},
|
||||
{
|
||||
"field": "status",
|
||||
"op": "=",
|
||||
"var": "status_input"
|
||||
},
|
||||
{
|
||||
"field": "priority",
|
||||
"op": "=",
|
||||
"var": "priority_input"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,44 +1,50 @@
|
||||
{
|
||||
"tblname": "sd_test_plans",
|
||||
"title": "测试方案",
|
||||
"params": {
|
||||
"sortby": ["created_at desc"],
|
||||
"confidential_fields": [],
|
||||
"browserfields": {
|
||||
"alters": {
|
||||
"iteration_id": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_iteration_options.dspy')}}",
|
||||
"valueField": "iteration_id",
|
||||
"textField": "iteration_id_text"
|
||||
},
|
||||
"plan_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_plan_type.dspy')}}",
|
||||
"valueField": "plan_type",
|
||||
"textField": "plan_type_text"
|
||||
},
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_plan_status.dspy')}}",
|
||||
"valueField": "status",
|
||||
"textField": "status_text"
|
||||
}
|
||||
}
|
||||
"tblname": "sd_test_plans",
|
||||
"title": "测试方案",
|
||||
"params": {
|
||||
"sortby": [
|
||||
"created_at desc"
|
||||
],
|
||||
"confidential_fields": [],
|
||||
"browserfields": {
|
||||
"alters": {
|
||||
"iteration_id": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_iteration_options.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"editexclouded": ["id", "created_at", "updated_at"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_test_plan_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_test_plan_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_test_plan_delete.dspy')}}"
|
||||
"plan_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_plan_type.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"subtables": [
|
||||
{
|
||||
"field": "plan_id",
|
||||
"title": "测试用例",
|
||||
"url": "{{entire_url('../sd_test_case_list?plan_id=${id}')}}",
|
||||
"subtable": "sd_test_cases"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_plan_status.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editexclouded": [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at"
|
||||
],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/sd_test_plan_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/sd_test_plan_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/sd_test_plan_delete.dspy')}}"
|
||||
},
|
||||
"subtables": [
|
||||
{
|
||||
"field": "plan_id",
|
||||
"title": "测试用例",
|
||||
"url": "{{entire_url('../sd_test_case_list?plan_id=${id}')}}",
|
||||
"subtable": "sd_test_cases"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,117 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
RBAC path registration for pipeline-sdlc module.
|
||||
Run: cd ~/repos/sage && ./py3/bin/python ~/repos/pipeline-sdlc/scripts/load_path.py
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
"""RBAC path registration for pipeline-sdlc module."""
|
||||
import os, sys, subprocess
|
||||
|
||||
MOD = "pipeline_sdlc"
|
||||
MOD = "pipeline-sdlc"
|
||||
|
||||
PATHS_ANY = [
|
||||
f"/{MOD}/index.ui",
|
||||
f"/{MOD}/styles/sdlc.css",
|
||||
]
|
||||
|
||||
PATHS_LOGINED = [
|
||||
f"/{MOD}",
|
||||
f"/{MOD}/sd_project",
|
||||
f"/{MOD}/sd_project/index.ui",
|
||||
f"/{MOD}/sd_project/get_sd_project_list.dspy",
|
||||
f"/{MOD}/sd_project/add_sd_project_list.dspy",
|
||||
f"/{MOD}/sd_project/update_sd_project_list.dspy",
|
||||
f"/{MOD}/sd_project/delete_sd_project_list.dspy",
|
||||
f"/{MOD}/sd_iteration",
|
||||
f"/{MOD}/sd_iteration/index.ui",
|
||||
f"/{MOD}/sd_iteration/get_sd_iteration_list.dspy",
|
||||
f"/{MOD}/sd_iteration/add_sd_iteration_list.dspy",
|
||||
f"/{MOD}/sd_iteration/update_sd_iteration_list.dspy",
|
||||
f"/{MOD}/sd_iteration/delete_sd_iteration_list.dspy",
|
||||
f"/{MOD}/sd_test_plan",
|
||||
f"/{MOD}/sd_test_plan/index.ui",
|
||||
f"/{MOD}/sd_test_plan/get_sd_test_plan_list.dspy",
|
||||
f"/{MOD}/sd_test_plan/add_sd_test_plan_list.dspy",
|
||||
f"/{MOD}/sd_test_plan/update_sd_test_plan_list.dspy",
|
||||
f"/{MOD}/sd_test_plan/delete_sd_test_plan_list.dspy",
|
||||
f"/{MOD}/sd_test_case",
|
||||
f"/{MOD}/sd_test_case/index.ui",
|
||||
f"/{MOD}/sd_test_case/get_sd_test_case_list.dspy",
|
||||
f"/{MOD}/sd_test_case/add_sd_test_case_list.dspy",
|
||||
f"/{MOD}/sd_test_case/update_sd_test_case_list.dspy",
|
||||
f"/{MOD}/sd_test_case/delete_sd_test_case_list.dspy",
|
||||
f"/{MOD}/sd_bug",
|
||||
f"/{MOD}/sd_bug/index.ui",
|
||||
f"/{MOD}/sd_bug/get_sd_bug_list.dspy",
|
||||
f"/{MOD}/sd_bug/add_sd_bug_list.dspy",
|
||||
f"/{MOD}/sd_bug/update_sd_bug_list.dspy",
|
||||
f"/{MOD}/sd_bug/delete_sd_bug_list.dspy",
|
||||
f"/{MOD}/sd_deploy_env",
|
||||
f"/{MOD}/sd_deploy_env/index.ui",
|
||||
f"/{MOD}/sd_deploy_env/get_sd_deploy_env_list.dspy",
|
||||
f"/{MOD}/sd_deploy_env/add_sd_deploy_env_list.dspy",
|
||||
f"/{MOD}/sd_deploy_env/update_sd_deploy_env_list.dspy",
|
||||
f"/{MOD}/sd_deploy_env/delete_sd_deploy_env_list.dspy",
|
||||
# API endpoints
|
||||
f"/{MOD}/api/create_sd_project.dspy",
|
||||
f"/{MOD}/api/update_sd_project.dspy",
|
||||
f"/{MOD}/api/delete_sd_project.dspy",
|
||||
f"/{MOD}/api/create_sd_iteration.dspy",
|
||||
f"/{MOD}/api/update_sd_iteration.dspy",
|
||||
f"/{MOD}/api/delete_sd_iteration.dspy",
|
||||
f"/{MOD}/api/create_sd_bug.dspy",
|
||||
f"/{MOD}/api/update_sd_bug.dspy",
|
||||
f"/{MOD}/api/close_sd_bug.dspy",
|
||||
f"/{MOD}/api/save_sd_deploy_env.dspy",
|
||||
f"/{MOD}/api/get_project_options.dspy",
|
||||
f"/{MOD}/api/get_iteration_options.dspy",
|
||||
f"/{MOD}/api/check_iteration_bugs.dspy",
|
||||
f"/{MOD}/api/submit_bug.dspy",
|
||||
# Dashboard
|
||||
f"/{MOD}/sd_dashboard/dashboard.ui",
|
||||
f"/{MOD}/sd_dashboard/index.ui",
|
||||
f"/{MOD}/sd_dashboard/dashboard_data.dspy",
|
||||
# Projects
|
||||
f"/{MOD}/sd_projects/index.ui",
|
||||
f"/{MOD}/sd_projects/get_sd_projects.dspy",
|
||||
f"/{MOD}/sd_projects/add_sd_projects.dspy",
|
||||
f"/{MOD}/sd_projects/update_sd_projects.dspy",
|
||||
f"/{MOD}/sd_projects/delete_sd_projects.dspy",
|
||||
f"/{MOD}/sd_project_list/index.ui",
|
||||
# Iterations
|
||||
f"/{MOD}/sd_iterations/index.ui",
|
||||
f"/{MOD}/sd_iterations/get_sd_iterations.dspy",
|
||||
f"/{MOD}/sd_iterations/add_sd_iterations.dspy",
|
||||
f"/{MOD}/sd_iterations/update_sd_iterations.dspy",
|
||||
f"/{MOD}/sd_iterations/delete_sd_iterations.dspy",
|
||||
f"/{MOD}/sd_iteration_list/index.ui",
|
||||
# Test Plans
|
||||
f"/{MOD}/sd_test_plans/index.ui",
|
||||
f"/{MOD}/sd_test_plans/get_sd_test_plans.dspy",
|
||||
f"/{MOD}/sd_test_plans/add_sd_test_plans.dspy",
|
||||
f"/{MOD}/sd_test_plans/update_sd_test_plans.dspy",
|
||||
f"/{MOD}/sd_test_plans/delete_sd_test_plans.dspy",
|
||||
f"/{MOD}/sd_test_plan_list/index.ui",
|
||||
# Test Cases
|
||||
f"/{MOD}/sd_test_cases/index.ui",
|
||||
f"/{MOD}/sd_test_cases/get_sd_test_cases.dspy",
|
||||
f"/{MOD}/sd_test_cases/add_sd_test_cases.dspy",
|
||||
f"/{MOD}/sd_test_cases/update_sd_test_cases.dspy",
|
||||
f"/{MOD}/sd_test_cases/delete_sd_test_cases.dspy",
|
||||
f"/{MOD}/sd_test_case_list/index.ui",
|
||||
# Bugs
|
||||
f"/{MOD}/sd_bugs/index.ui",
|
||||
f"/{MOD}/sd_bugs/get_sd_bugs.dspy",
|
||||
f"/{MOD}/sd_bugs/add_sd_bugs.dspy",
|
||||
f"/{MOD}/sd_bugs/update_sd_bugs.dspy",
|
||||
f"/{MOD}/sd_bugs/delete_sd_bugs.dspy",
|
||||
f"/{MOD}/sd_bug_list/index.ui",
|
||||
# Deploy Envs
|
||||
f"/{MOD}/sd_deploy_envs/index.ui",
|
||||
f"/{MOD}/sd_deploy_envs/get_sd_deploy_envs.dspy",
|
||||
f"/{MOD}/sd_deploy_envs/add_sd_deploy_envs.dspy",
|
||||
f"/{MOD}/sd_deploy_envs/update_sd_deploy_envs.dspy",
|
||||
f"/{MOD}/sd_deploy_envs/delete_sd_deploy_envs.dspy",
|
||||
f"/{MOD}/sd_deploy_env_list/index.ui",
|
||||
# Review
|
||||
f"/{MOD}/sd_review/index.ui",
|
||||
# API
|
||||
f"/{MOD}/api/bug_close.dspy",
|
||||
f"/{MOD}/api/bug_confirm.dspy",
|
||||
f"/{MOD}/api/check_iteration_bugs.dspy",
|
||||
f"/{MOD}/api/env_verify.dspy",
|
||||
f"/{MOD}/api/get_iteration_options.dspy",
|
||||
f"/{MOD}/api/get_project_options.dspy",
|
||||
f"/{MOD}/api/get_test_plan_options.dspy",
|
||||
f"/{MOD}/api/submit_bug.dspy",
|
||||
f"/{MOD}/api/sd_bug_create.dspy",
|
||||
f"/{MOD}/api/sd_bug_update.dspy",
|
||||
f"/{MOD}/api/sd_bug_delete.dspy",
|
||||
f"/{MOD}/api/sd_case_create.dspy",
|
||||
f"/{MOD}/api/sd_case_update.dspy",
|
||||
f"/{MOD}/api/sd_case_delete.dspy",
|
||||
f"/{MOD}/api/sd_env_create.dspy",
|
||||
f"/{MOD}/api/sd_env_update.dspy",
|
||||
f"/{MOD}/api/sd_env_delete.dspy",
|
||||
f"/{MOD}/api/sd_iter_create.dspy",
|
||||
f"/{MOD}/api/sd_iter_update.dspy",
|
||||
f"/{MOD}/api/sd_iter_delete.dspy",
|
||||
f"/{MOD}/api/sd_plan_create.dspy",
|
||||
f"/{MOD}/api/sd_plan_update.dspy",
|
||||
f"/{MOD}/api/sd_plan_delete.dspy",
|
||||
f"/{MOD}/api/sd_proj_create.dspy",
|
||||
f"/{MOD}/api/sd_proj_update.dspy",
|
||||
f"/{MOD}/api/sd_proj_delete.dspy",
|
||||
# Search data sources
|
||||
f"/{MOD}/api/get_search_bug_status.dspy",
|
||||
f"/{MOD}/api/get_search_case_status.dspy",
|
||||
f"/{MOD}/api/get_search_case_type.dspy",
|
||||
f"/{MOD}/api/get_search_env_status.dspy",
|
||||
f"/{MOD}/api/get_search_env_type.dspy",
|
||||
f"/{MOD}/api/get_search_iteration_status.dspy",
|
||||
f"/{MOD}/api/get_search_iteration_type.dspy",
|
||||
f"/{MOD}/api/get_search_plan_status.dspy",
|
||||
f"/{MOD}/api/get_search_plan_type.dspy",
|
||||
f"/{MOD}/api/get_search_priority.dspy",
|
||||
f"/{MOD}/api/get_search_project_type.dspy",
|
||||
f"/{MOD}/api/get_search_reporter_type.dspy",
|
||||
f"/{MOD}/api/get_search_severity.dspy",
|
||||
f"/{MOD}/api/get_search_status.dspy",
|
||||
]
|
||||
|
||||
|
||||
def find_sage_root():
|
||||
"""Find Sage root directory."""
|
||||
for candidate in [
|
||||
os.path.expanduser("~/repos/sage"),
|
||||
os.path.expanduser("~/sage"),
|
||||
]:
|
||||
if os.path.isdir(os.path.join(candidate, "wwwroot")):
|
||||
return candidate
|
||||
def find_app_root():
|
||||
for c in [os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
os.path.expanduser("~/pipeline")]:
|
||||
if os.path.isfile(os.path.join(c, "set_role_perm.py")):
|
||||
return c
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
sage_root = find_sage_root()
|
||||
if not sage_root:
|
||||
print("ERROR: Sage root not found")
|
||||
root = find_app_root()
|
||||
if not root:
|
||||
print("ERROR: pipeline root not found")
|
||||
sys.exit(1)
|
||||
|
||||
set_perm = os.path.join(sage_root, "scripts", "set_role_perm.py")
|
||||
if not os.path.exists(set_perm):
|
||||
set_perm = os.path.join(sage_root, "set_role_perm.py")
|
||||
|
||||
if not os.path.exists(set_perm):
|
||||
print(f"ERROR: set_role_perm.py not found in {sage_root}")
|
||||
sys.exit(1)
|
||||
|
||||
python = os.path.join(sage_root, "py3", "bin", "python")
|
||||
if not os.path.exists(python):
|
||||
python = sys.executable
|
||||
|
||||
set_perm = os.path.join(root, "set_role_perm.py")
|
||||
py = sys.executable
|
||||
count = 0
|
||||
for role, paths in [("any", PATHS_ANY), ("logined", PATHS_LOGINED)]:
|
||||
for path in paths:
|
||||
cmd = [python, set_perm, role, path]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, cwd=sage_root)
|
||||
if result.returncode == 0:
|
||||
cmd = [py, set_perm, role, path]
|
||||
r = subprocess.run(cmd, capture_output=True, text=True, cwd=root)
|
||||
if r.returncode == 0:
|
||||
count += 1
|
||||
else:
|
||||
print(f"WARN: {path} ({role}): {result.stderr.strip()}")
|
||||
|
||||
print(f"Registered {count} paths for module '{MOD}'")
|
||||
print(f" WARN: {path} -> {r.stderr.strip()[-80:]}")
|
||||
print(f"Registered {count}/{len(PATHS_ANY)+len(PATHS_LOGINED)} paths for {MOD}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@ -1,58 +1,241 @@
|
||||
# cockpit_chat.dspy - Send message or list conversation
|
||||
# POST: action=send_message, iteration_id, message_text
|
||||
# GET: action=list_messages, iteration_id (optional), task_id (optional)
|
||||
# cockpit_chat.dspy - Product-grade LLM conversation with context
|
||||
# POST: action=send_message, iteration_id, message_text, model_id, file_paths
|
||||
# GET: action=list_messages, iteration_id, task_id
|
||||
|
||||
import aiohttp
|
||||
|
||||
action = (params_kw or {}).get('action', 'list_messages')
|
||||
dbname = get_module_dbname('pipeline-sdlc')
|
||||
|
||||
DEFAULT_SYSTEM_PROMPT = """你是一个专业的软件开发 Agent,名为「开发产线驾驶舱」。你的职责是帮助用户完成软件开发生命周期的各个环节:需求分析、设计、编码、测试、部署。
|
||||
|
||||
对话规则:
|
||||
1. 简洁专业,用中文回复
|
||||
2. 当用户描述需求时,先理解并复述确认,然后给出分析和建议
|
||||
3. 如果用户提到了项目/迭代,主动关联上下文
|
||||
4. 可以建议启动开发产线来推进工作
|
||||
5. 对于代码相关问题,给出具体的代码示例
|
||||
6. 记住对话历史,保持上下文连贯
|
||||
|
||||
当前你可以帮助用户完成:
|
||||
- 创建和管理项目、迭代
|
||||
- 数据表设计、CRUD 设计、API 设计
|
||||
- 代码生成、规范检查、自动修复
|
||||
- 测试用例生成、功能测试、Bug 管理
|
||||
- 环境部署和验证"""
|
||||
|
||||
|
||||
async def _load_agent_settings(sor, uid):
|
||||
"""Load user's agent settings, return defaults if not set."""
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT default_llm_id, system_prompt, temperature, max_context_messages FROM sd_agent_settings WHERE user_id=${uid}$",
|
||||
{"uid": uid}
|
||||
)
|
||||
if recs:
|
||||
r = recs[0]
|
||||
return {
|
||||
'llm_id': getattr(r, 'default_llm_id', None),
|
||||
'system_prompt': getattr(r, 'system_prompt', None) or DEFAULT_SYSTEM_PROMPT,
|
||||
'temperature': float(getattr(r, 'temperature', 0.7) or 0.7),
|
||||
'max_context': int(getattr(r, 'max_context_messages', 30) or 30),
|
||||
}
|
||||
return {
|
||||
'llm_id': None,
|
||||
'system_prompt': DEFAULT_SYSTEM_PROMPT,
|
||||
'temperature': 0.7,
|
||||
'max_context': 30,
|
||||
}
|
||||
|
||||
|
||||
async def _select_model(sor, preferred_llm_id, has_files):
|
||||
"""Select best model: prefer user choice, then multimodal if files, else first active text."""
|
||||
# If user has preferred model, use it
|
||||
if preferred_llm_id:
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT id, name, provider, model_id, api_base, api_key, capabilities FROM llm WHERE id=${lid}$ AND status='active'",
|
||||
{"lid": preferred_llm_id}
|
||||
)
|
||||
if recs:
|
||||
return recs[0]
|
||||
|
||||
# Auto-select based on file presence
|
||||
if has_files:
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT id, name, provider, model_id, api_base, api_key, capabilities FROM llm WHERE status='active' AND capabilities LIKE '%multimodal%' LIMIT 1",
|
||||
{}
|
||||
)
|
||||
if recs:
|
||||
return recs[0]
|
||||
|
||||
# Fallback: first active model
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT id, name, provider, model_id, api_base, api_key, capabilities FROM llm WHERE status='active' LIMIT 1",
|
||||
{}
|
||||
)
|
||||
if recs:
|
||||
return recs[0]
|
||||
return None
|
||||
|
||||
|
||||
async def _build_context(sor, iteration_id, task_id, max_msgs, system_prompt):
|
||||
"""Build LLM messages array with Hermes-style context."""
|
||||
messages = [{"role": "system", "content": system_prompt}]
|
||||
|
||||
# Project/iteration context
|
||||
if iteration_id:
|
||||
iters = await sor.sqlExe(
|
||||
"SELECT i.iteration_name, i.iteration_type, i.status, i.scope, p.name as project_name, p.description, p.tech_stack "
|
||||
"FROM sd_iterations i LEFT JOIN sd_projects p ON i.project_id=p.id WHERE i.id=${iid}$",
|
||||
{"iid": iteration_id}
|
||||
)
|
||||
if iters:
|
||||
it = iters[0]
|
||||
ctx_parts = ["## 当前上下文"]
|
||||
ctx_parts.append(f"项目: {getattr(it, 'project_name', '未知')}")
|
||||
ctx_parts.append(f"迭代: {getattr(it, 'iteration_name', '未知')}")
|
||||
ctx_parts.append(f"类型: {getattr(it, 'iteration_type', '')}")
|
||||
ctx_parts.append(f"状态: {getattr(it, 'status', '')}")
|
||||
desc = getattr(it, 'description', '')
|
||||
if desc:
|
||||
ctx_parts.append(f"项目描述: {desc[:500]}")
|
||||
stack = getattr(it, 'tech_stack', '')
|
||||
if stack:
|
||||
ctx_parts.append(f"技术栈: {stack[:300]}")
|
||||
scope = getattr(it, 'scope', '')
|
||||
if scope:
|
||||
ctx_parts.append(f"迭代范围: {scope[:500]}")
|
||||
messages.append({"role": "system", "content": "\n".join(ctx_parts)})
|
||||
|
||||
# Task context
|
||||
if task_id:
|
||||
tasks = await sor.sqlExe(
|
||||
"SELECT id, status, pipeline_id FROM pipeline_tasks WHERE id=${tid}$",
|
||||
{"tid": task_id}
|
||||
)
|
||||
if tasks:
|
||||
t = tasks[0]
|
||||
messages.append({"role": "system", "content": f"关联 Pipeline 任务: {t.id}, 状态: {getattr(t, 'status', 'unknown')}"})
|
||||
|
||||
# Conversation history
|
||||
where = []
|
||||
params = {}
|
||||
if task_id:
|
||||
where.append("task_id=${tid}$")
|
||||
params["tid"] = task_id
|
||||
if iteration_id:
|
||||
where.append("iteration_id=${iid}$")
|
||||
params["iid"] = iteration_id
|
||||
|
||||
if where:
|
||||
sql = f"SELECT role, content FROM sd_conversations WHERE {' OR '.join(where)} ORDER BY created_at DESC LIMIT ${max_msgs}$"
|
||||
params["max_msgs"] = max_msgs
|
||||
history = await sor.sqlExe(sql, params)
|
||||
# Reverse to chronological order
|
||||
for h in reversed(history):
|
||||
role = getattr(h, 'role', 'user')
|
||||
content = getattr(h, 'content', '')
|
||||
if role in ('user', 'agent'):
|
||||
messages.append({"role": "user" if role == "user" else "assistant", "content": content})
|
||||
|
||||
return messages
|
||||
|
||||
|
||||
async def _call_llm(model_info, messages, temperature):
|
||||
"""Call LLM API directly using model config from llm table."""
|
||||
api_base = model_info.api_base.rstrip('/')
|
||||
api_key = model_info.api_key
|
||||
model_id = model_info.model_id
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
payload = {
|
||||
"model": model_id,
|
||||
"messages": messages,
|
||||
"temperature": temperature,
|
||||
}
|
||||
|
||||
timeout = aiohttp.ClientTimeout(total=120)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.post(f"{api_base}/chat/completions", headers=headers, json=payload) as resp:
|
||||
if resp.status != 200:
|
||||
text = await resp.text()
|
||||
raise ValueError(f"LLM API error {resp.status}: {text[:300]}")
|
||||
data = await resp.json()
|
||||
content = data["choices"][0]["message"]["content"]
|
||||
# Truncate overly long responses
|
||||
if len(content) > 8000:
|
||||
content = content[:8000] + "\n\n...(内容过长已截断)"
|
||||
return content
|
||||
|
||||
|
||||
# ==================== ACTION HANDLERS ====================
|
||||
|
||||
if action == 'send_message':
|
||||
iteration_id = (params_kw or {}).get('iteration_id', '')
|
||||
task_id = (params_kw or {}).get('task_id', '')
|
||||
message_text = (params_kw or {}).get('message_text', '').strip()
|
||||
model_id = (params_kw or {}).get('model_id', '')
|
||||
file_paths_raw = (params_kw or {}).get('file_paths', '[]')
|
||||
|
||||
if not iteration_id or not message_text:
|
||||
return json.dumps({"error": "Missing iteration_id or message_text"}, ensure_ascii=False)
|
||||
if not message_text:
|
||||
return json.dumps({"error": "message_text is required"}, ensure_ascii=False)
|
||||
|
||||
# Parse file paths
|
||||
has_files = False
|
||||
try:
|
||||
fps = json.loads(file_paths_raw)
|
||||
has_files = len(fps) > 0
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
uid = await get_user()
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
# Save user message
|
||||
# 1. Save user message
|
||||
msg_id = getID()
|
||||
await sor.C('sd_conversations', {
|
||||
'id': msg_id,
|
||||
'iteration_id': iteration_id,
|
||||
'iteration_id': iteration_id or '',
|
||||
'task_id': task_id or '',
|
||||
'step_name': '',
|
||||
'role': 'user',
|
||||
'content': message_text,
|
||||
'attachments': '[]',
|
||||
'attachments': file_paths_raw,
|
||||
'msg_type': 'text',
|
||||
'org_id': '0',
|
||||
'created_by': uid
|
||||
})
|
||||
|
||||
# Generate agent response
|
||||
# 1. Check if there's an active pipeline task for this iteration
|
||||
if task_id:
|
||||
agent_reply = f"收到你的消息。当前任务 {task_id} 正在执行中,你的输入已记录。"
|
||||
else:
|
||||
# Check for existing iterations
|
||||
iters = await sor.sqlExe(
|
||||
"SELECT id, task_id, status FROM sd_iterations WHERE id=${iid}$", {"iid": iteration_id})
|
||||
if iters:
|
||||
it = iters[0]
|
||||
if hasattr(it, 'task_id') and it.task_id:
|
||||
agent_reply = f"收到。迭代任务 {it.task_id} 状态: {it.status}。"
|
||||
else:
|
||||
agent_reply = f"收到你的需求:「{message_text[:50]}...」。请确认是否要为此迭代启动开发产线?"
|
||||
else:
|
||||
agent_reply = f"收到你的需求:「{message_text[:50]}...」。建议先在项目管理中创建项目和迭代。"
|
||||
# 2. Load agent settings
|
||||
settings = await _load_agent_settings(sor, uid)
|
||||
|
||||
# Save agent message
|
||||
# 3. Select model (preferred → auto by file type → first active)
|
||||
selected_llm_id = model_id or settings['llm_id']
|
||||
model_info = await _select_model(sor, selected_llm_id, has_files)
|
||||
if not model_info:
|
||||
return json.dumps({"error": "No active LLM model configured"}, ensure_ascii=False)
|
||||
|
||||
# 4. Build context
|
||||
messages = await _build_context(
|
||||
sor, iteration_id, task_id,
|
||||
settings['max_context'], settings['system_prompt']
|
||||
)
|
||||
# Append current user message
|
||||
messages.append({"role": "user", "content": message_text})
|
||||
|
||||
# 5. Call LLM
|
||||
try:
|
||||
agent_reply = await _call_llm(model_info, messages, settings['temperature'])
|
||||
except Exception as e:
|
||||
agent_reply = f"抱歉,模型调用失败: {str(e)[:200]}"
|
||||
|
||||
# 6. Save agent response
|
||||
agent_msg_id = getID()
|
||||
await sor.C('sd_conversations', {
|
||||
'id': agent_msg_id,
|
||||
'iteration_id': iteration_id,
|
||||
'iteration_id': iteration_id or '',
|
||||
'task_id': task_id or '',
|
||||
'step_name': '',
|
||||
'role': 'agent',
|
||||
@ -63,10 +246,16 @@ if action == 'send_message':
|
||||
'created_by': 'system'
|
||||
})
|
||||
|
||||
return json.dumps({"success": True, "message_id": msg_id}, ensure_ascii=False)
|
||||
return json.dumps({
|
||||
"success": True,
|
||||
"message_id": msg_id,
|
||||
"agent_reply": agent_reply,
|
||||
"model_used": model_info.name,
|
||||
}, ensure_ascii=False)
|
||||
|
||||
|
||||
else:
|
||||
# list_messages - return messages as Bricks widget JSON
|
||||
# list_messages - return conversation as Bricks widget JSON
|
||||
iteration_id = (params_kw or {}).get('iteration_id', '')
|
||||
task_id = (params_kw or {}).get('task_id', '')
|
||||
|
||||
@ -85,7 +274,6 @@ else:
|
||||
sql = f"SELECT role, content, msg_type, created_at FROM sd_conversations WHERE {' OR '.join(where)} ORDER BY created_at ASC LIMIT 50"
|
||||
msgs = await sor.sqlExe(sql, params)
|
||||
|
||||
# Build chat messages widget
|
||||
msg_widgets = []
|
||||
for m in msgs:
|
||||
role = m.role if hasattr(m, 'role') else ''
|
||||
@ -99,12 +287,12 @@ else:
|
||||
elif role == 'user':
|
||||
bg = '#dbeafe'
|
||||
align = 'flex-end'
|
||||
label = '你'
|
||||
label = '\u4f60'
|
||||
label_color = '#2563eb'
|
||||
else:
|
||||
bg = '#f1f5f9'
|
||||
align = 'center'
|
||||
label = '系统'
|
||||
label = '\u7cfb\u7edf'
|
||||
label_color = '#94a3b8'
|
||||
|
||||
msg_widgets.append({
|
||||
@ -134,7 +322,7 @@ else:
|
||||
msg_widgets.append({
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "暂无对话记录。选择一个迭代后,在下方输入框中开始对话。",
|
||||
"text": "\u6682\u65e0\u5bf9\u8bdd\u8bb0\u5f55\u3002\u9009\u62e9\u4e00\u4e2a\u8fed\u4ee3\u540e\uff0c\u5728\u4e0b\u65b9\u8f93\u5165\u6846\u4e2d\u5f00\u59cb\u5bf9\u8bdd\u3002",
|
||||
"cfontsize": 0.9, "color": "#94a3b8", "padding": "20px"
|
||||
}
|
||||
})
|
||||
|
||||
22
wwwroot/api/cockpit_model_options.dspy
Normal file
22
wwwroot/api/cockpit_model_options.dspy
Normal file
@ -0,0 +1,22 @@
|
||||
# cockpit_model_options.dspy - Returns active models with capabilities for dropdown
|
||||
# Used by cockpit model selector
|
||||
|
||||
dbname = get_module_dbname('pipeline-sdlc')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT id, name, provider, model_id, capabilities FROM llm WHERE status='active' ORDER BY name",
|
||||
{}
|
||||
)
|
||||
|
||||
rows = []
|
||||
for r in recs:
|
||||
rows.append({
|
||||
'value': r.id,
|
||||
'text': f"{r.name} ({r.provider})",
|
||||
'provider': r.provider,
|
||||
'model_id': r.model_id,
|
||||
'capabilities': r.capabilities or 'text',
|
||||
})
|
||||
|
||||
return rows
|
||||
10
wwwroot/api/get_pipeline_options.dspy
Normal file
10
wwwroot/api/get_pipeline_options.dspy
Normal file
@ -0,0 +1,10 @@
|
||||
import json
|
||||
result = json.dumps([{'value': '', 'text': '请选择'}], ensure_ascii=False)
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'pipeline') as sor:
|
||||
rows = await sor.sqlExe("select id as value, name as text from pipelines order by name")
|
||||
r = [{'value': '', 'text': '请选择'}] + list(rows)
|
||||
return json.dumps(r, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
debug(f'get_pipeline_options error: {e}')
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
@ -16,12 +16,12 @@
|
||||
"gap": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{ "widgettype": "Title2", "options": { "text": "开发产线驾驶舱" } },
|
||||
{ "widgettype": "Text", "options": { "text": "AI驱动的对话式开发", "cfontsize": 0.9, "color": "#94a3b8" } },
|
||||
{ "widgettype": "Title2", "options": { "text": "\u5f00\u53d1\u4ea7\u7ebf\u9a7e\u9a76\u8231" } },
|
||||
{ "widgettype": "Text", "options": { "text": "AI\u9a71\u52a8\u7684\u5bf9\u8bdd\u5f0f\u5f00\u53d1", "cfontsize": 0.9, "color": "#94a3b8" } },
|
||||
{ "widgettype": "Filler" },
|
||||
{
|
||||
"widgettype": "Button",
|
||||
"options": { "name": "refresh", "label": "刷新", "css": "small" },
|
||||
"options": { "name": "refresh", "label": "\u5237\u65b0", "css": "small" },
|
||||
"binds": [{
|
||||
"wid": "self", "event": "click", "actiontype": "script", "target": "self",
|
||||
"script": "location.reload();"
|
||||
@ -76,8 +76,8 @@
|
||||
"widgettype": "VBox",
|
||||
"options": { "alignItems": "center", "padding": "40px 0", "gap": "12px" },
|
||||
"subwidgets": [
|
||||
{ "widgettype": "Text", "options": { "text": "选择一个迭代后,在下方输入你的需求。", "cfontsize": 1, "color": "#64748b" } },
|
||||
{ "widgettype": "Text", "options": { "text": "Agent 将自动分析需求、生成设计、驱动产线执行。", "cfontsize": 0.85, "color": "#94a3b8" } }
|
||||
{ "widgettype": "Text", "options": { "text": "\u9009\u62e9\u4e00\u4e2a\u8fed\u4ee3\u540e\uff0c\u5728\u4e0b\u65b9\u8f93\u5165\u4f60\u7684\u9700\u6c42\u3002", "cfontsize": 1, "color": "#64748b" } },
|
||||
{ "widgettype": "Text", "options": { "text": "Agent \u5c06\u81ea\u52a8\u5206\u6790\u9700\u6c42\u3001\u751f\u6210\u8bbe\u8ba1\u3001\u9a71\u52a8\u4ea7\u7ebf\u6267\u884c\u3002", "cfontsize": 0.85, "color": "#94a3b8" } }
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -98,7 +98,7 @@
|
||||
"event": "inputed",
|
||||
"actiontype": "script",
|
||||
"target": "self",
|
||||
"script": "var prompt=params.prompt;var files=params.add_files||[];var iid='';try{var iter=bricks.getWidgetById('current_iteration_id',bricks.app);if(iter)iid=iter.options.value||'';}catch(e){}var chat=bricks.getWidgetById('chat_scroll',bricks.app);if(chat){var ub=new bricks.HBox({width:'100%'});var um=new bricks.VBox({width:'85%',alignSelf:'flex-end',bgcolor:'#dbeafe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});um.add_widget(new bricks.Text({text:'\u4f60',cfontsize:0.75,color:'#2563eb',fontWeight:'bold'}));um.add_widget(new bricks.Text({text:prompt,cfontsize:0.95,color:'#1e293b',whiteSpace:'pre-wrap'}));ub.add_widget(new bricks.VBox({css:'filler'}));ub.add_widget(um);ub.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/chat-user.svg')}));chat.add_widget(ub);var ab=new bricks.HBox({width:'100%'});var am=new bricks.VBox({width:'85%',alignSelf:'flex-start',bgcolor:'#e8f0fe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});am.add_widget(new bricks.Text({text:'Agent',cfontsize:0.75,color:'#3b82f6',fontWeight:'bold'}));var at=new bricks.Text({text:'\u6b63\u5728\u5206\u6790\u9700\u6c42...',cfontsize:0.85,color:'#64748b'});am.add_widget(at);ab.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/llm.svg')}));ab.add_widget(am);ab.add_widget(new bricks.VBox({css:'filler'}));chat.add_widget(ab);}var body='message_text='+encodeURIComponent(prompt)+'&iteration_id='+encodeURIComponent(iid)+'&action=send_message&file_paths='+encodeURIComponent(JSON.stringify(files.map(function(f){return f.name||f})));fetch('/pipeline-sdlc/api/cockpit_chat.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body}).then(function(r){return r.json()}).then(function(r){if(r.success){location.reload();}else{if(at)at.set_text('\u9519\u8bef: '+(r.error||'\u672a\u77e5\u9519\u8bef'));}});"
|
||||
"script": "var p=params.prompt;var files=params.add_files||[];var iid='';try{var iter=bricks.getWidgetById('current_iteration_id',bricks.app);if(iter)iid=iter.options.value||'';}catch(e){}var chat=bricks.getWidgetById('chat_scroll',bricks.app);var at=null;if(chat){var ub=new bricks.HBox({width:'100%'});var um=new bricks.VBox({width:'85%',alignSelf:'flex-end',bgcolor:'#dbeafe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});um.add_widget(new bricks.Text({text:'\\u4f60',cfontsize:0.75,color:'#2563eb',fontWeight:'bold'}));um.add_widget(new bricks.Text({text:p,cfontsize:0.95,color:'#1e293b',whiteSpace:'pre-wrap'}));ub.add_widget(new bricks.VBox({css:'filler'}));ub.add_widget(um);ub.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/chat-user.svg')}));chat.add_widget(ub);var ab=new bricks.HBox({width:'100%'});var am=new bricks.VBox({width:'85%',alignSelf:'flex-start',bgcolor:'#e8f0fe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});am.add_widget(new bricks.Text({text:'Agent',cfontsize:0.75,color:'#3b82f6',fontWeight:'bold'}));at=new bricks.Text({text:'\\u6b63\\u5728\\u5206\\u6790\\u9700\\u6c42...',cfontsize:0.85,color:'#64748b'});am.add_widget(at);ab.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/llm.svg')}));ab.add_widget(am);ab.add_widget(new bricks.VBox({css:'filler'}));chat.add_widget(ab);}var body='message_text='+encodeURIComponent(p)+'&iteration_id='+encodeURIComponent(iid)+'&action=send_message&file_paths='+encodeURIComponent(JSON.stringify(files.map(function(f){return f.name||f})));fetch('/pipeline-sdlc/api/cockpit_chat.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body}).then(function(r){return r.json()}).then(function(r){if(r.success){if(at)at.set_text(r.agent_reply||'\\u5df2\\u5904\\u7406');var s=bricks.getWidgetById('stats_row',bricks.app);if(s)s.render({});}else{if(at)at.set_text('\\u9519\\u8bef: '+(r.error||'\\u672a\\u77e5\\u9519\\u8bef'));}});"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1 +0,0 @@
|
||||
.
|
||||
@ -50,26 +50,26 @@
|
||||
"severity": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_severity.dspy')}}",
|
||||
"valueField": "severity",
|
||||
"textField": "severity_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"priority": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_priority.dspy')}}",
|
||||
"valueField": "priority",
|
||||
"textField": "priority_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_bug_status.dspy')}}",
|
||||
"valueField": "status",
|
||||
"textField": "status_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"reporter_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_reporter_type.dspy')}}",
|
||||
"valueField": "reporter_type",
|
||||
"textField": "reporter_type_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -107,7 +107,7 @@
|
||||
"valueField": "iteration_id",
|
||||
"textField": "iteration_id_text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "sd_iterations",
|
||||
"tblvalue": "id",
|
||||
"tbltext": "iteration_name",
|
||||
@ -165,15 +165,15 @@
|
||||
"default": "'major'",
|
||||
"label": "严重程度",
|
||||
"uitype": "code",
|
||||
"valueField": "severity",
|
||||
"textField": "severity_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "severity",
|
||||
"textField": "severity_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_bug_severity'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_severity.dspy')}}"
|
||||
@ -187,15 +187,15 @@
|
||||
"default": "'P1'",
|
||||
"label": "优先级",
|
||||
"uitype": "code",
|
||||
"valueField": "priority",
|
||||
"textField": "priority_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "priority",
|
||||
"textField": "priority_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_priority'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_priority.dspy')}}"
|
||||
@ -209,15 +209,15 @@
|
||||
"default": "'open'",
|
||||
"label": "Bug状态",
|
||||
"uitype": "code",
|
||||
"valueField": "status",
|
||||
"textField": "status_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "status",
|
||||
"textField": "status_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_bug_status'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_bug_status.dspy')}}"
|
||||
@ -230,15 +230,15 @@
|
||||
"nullable": "no",
|
||||
"label": "提交人类型",
|
||||
"uitype": "code",
|
||||
"valueField": "reporter_type",
|
||||
"textField": "reporter_type_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "reporter_type",
|
||||
"textField": "reporter_type_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_reporter_type'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_reporter_type.dspy')}}"
|
||||
|
||||
@ -16,12 +16,12 @@
|
||||
"gap": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{ "widgettype": "Title2", "options": { "text": "开发产线驾驶舱" } },
|
||||
{ "widgettype": "Text", "options": { "text": "AI驱动的对话式开发", "cfontsize": 0.9, "color": "#94a3b8" } },
|
||||
{ "widgettype": "Title2", "options": { "text": "\u5f00\u53d1\u4ea7\u7ebf\u9a7e\u9a76\u8231" } },
|
||||
{ "widgettype": "Text", "options": { "text": "AI\u9a71\u52a8\u7684\u5bf9\u8bdd\u5f0f\u5f00\u53d1", "cfontsize": 0.9, "color": "#94a3b8" } },
|
||||
{ "widgettype": "Filler" },
|
||||
{
|
||||
"widgettype": "Button",
|
||||
"options": { "name": "refresh", "label": "刷新", "css": "small" },
|
||||
"options": { "name": "refresh", "label": "\u5237\u65b0", "css": "small" },
|
||||
"binds": [{
|
||||
"wid": "self", "event": "click", "actiontype": "script", "target": "self",
|
||||
"script": "location.reload();"
|
||||
@ -76,8 +76,8 @@
|
||||
"widgettype": "VBox",
|
||||
"options": { "alignItems": "center", "padding": "40px 0", "gap": "12px" },
|
||||
"subwidgets": [
|
||||
{ "widgettype": "Text", "options": { "text": "选择一个迭代后,在下方输入你的需求。", "cfontsize": 1, "color": "#64748b" } },
|
||||
{ "widgettype": "Text", "options": { "text": "Agent 将自动分析需求、生成设计、驱动产线执行。", "cfontsize": 0.85, "color": "#94a3b8" } }
|
||||
{ "widgettype": "Text", "options": { "text": "\u9009\u62e9\u4e00\u4e2a\u8fed\u4ee3\u540e\uff0c\u5728\u4e0b\u65b9\u8f93\u5165\u4f60\u7684\u9700\u6c42\u3002", "cfontsize": 1, "color": "#64748b" } },
|
||||
{ "widgettype": "Text", "options": { "text": "Agent \u5c06\u81ea\u52a8\u5206\u6790\u9700\u6c42\u3001\u751f\u6210\u8bbe\u8ba1\u3001\u9a71\u52a8\u4ea7\u7ebf\u6267\u884c\u3002", "cfontsize": 0.85, "color": "#94a3b8" } }
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -98,7 +98,7 @@
|
||||
"event": "inputed",
|
||||
"actiontype": "script",
|
||||
"target": "self",
|
||||
"script": "var prompt=params.prompt;var files=params.add_files||[];var iid='';try{var iter=bricks.getWidgetById('current_iteration_id',bricks.app);if(iter)iid=iter.options.value||'';}catch(e){}var chat=bricks.getWidgetById('chat_scroll',bricks.app);if(chat){var ub=new bricks.HBox({width:'100%'});var um=new bricks.VBox({width:'85%',alignSelf:'flex-end',bgcolor:'#dbeafe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});um.add_widget(new bricks.Text({text:'\u4f60',cfontsize:0.75,color:'#2563eb',fontWeight:'bold'}));um.add_widget(new bricks.Text({text:prompt,cfontsize:0.95,color:'#1e293b',whiteSpace:'pre-wrap'}));ub.add_widget(new bricks.VBox({css:'filler'}));ub.add_widget(um);ub.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/chat-user.svg')}));chat.add_widget(ub);var ab=new bricks.HBox({width:'100%'});var am=new bricks.VBox({width:'85%',alignSelf:'flex-start',bgcolor:'#e8f0fe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});am.add_widget(new bricks.Text({text:'Agent',cfontsize:0.75,color:'#3b82f6',fontWeight:'bold'}));var at=new bricks.Text({text:'\u6b63\u5728\u5206\u6790\u9700\u6c42...',cfontsize:0.85,color:'#64748b'});am.add_widget(at);ab.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/llm.svg')}));ab.add_widget(am);ab.add_widget(new bricks.VBox({css:'filler'}));chat.add_widget(ab);chat.scrollTo(0,chat.dom_element.scrollHeight);}var body='message_text='+encodeURIComponent(prompt)+'&iteration_id='+encodeURIComponent(iid)+'&action=send_message&file_paths='+encodeURIComponent(JSON.stringify(files.map(function(f){return f.name||f})));fetch('/pipeline-sdlc/api/cockpit_chat.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body}).then(function(r){return r.json()}).then(function(r){if(r.success){location.reload();}else{if(at)at.set_text('\u9519\u8bef: '+(r.error||'\u672a\u77e5\u9519\u8bef'));}});"
|
||||
"script": "var p=params.prompt;var files=params.add_files||[];var iid='';try{var iter=bricks.getWidgetById('current_iteration_id',bricks.app);if(iter)iid=iter.options.value||'';}catch(e){}var chat=bricks.getWidgetById('chat_scroll',bricks.app);var at=null;if(chat){var ub=new bricks.HBox({width:'100%'});var um=new bricks.VBox({width:'85%',alignSelf:'flex-end',bgcolor:'#dbeafe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});um.add_widget(new bricks.Text({text:'\\u4f60',cfontsize:0.75,color:'#2563eb',fontWeight:'bold'}));um.add_widget(new bricks.Text({text:p,cfontsize:0.95,color:'#1e293b',whiteSpace:'pre-wrap'}));ub.add_widget(new bricks.VBox({css:'filler'}));ub.add_widget(um);ub.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/chat-user.svg')}));chat.add_widget(ub);var ab=new bricks.HBox({width:'100%'});var am=new bricks.VBox({width:'85%',alignSelf:'flex-start',bgcolor:'#e8f0fe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});am.add_widget(new bricks.Text({text:'Agent',cfontsize:0.75,color:'#3b82f6',fontWeight:'bold'}));at=new bricks.Text({text:'\\u6b63\\u5728\\u5206\\u6790\\u9700\\u6c42...',cfontsize:0.85,color:'#64748b'});am.add_widget(at);ab.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/llm.svg')}));ab.add_widget(am);ab.add_widget(new bricks.VBox({css:'filler'}));chat.add_widget(ab);}var body='message_text='+encodeURIComponent(p)+'&iteration_id='+encodeURIComponent(iid)+'&action=send_message&file_paths='+encodeURIComponent(JSON.stringify(files.map(function(f){return f.name||f})));fetch('/pipeline-sdlc/api/cockpit_chat.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body}).then(function(r){return r.json()}).then(function(r){if(r.success){if(at)at.set_text(r.agent_reply||'\\u5df2\\u5904\\u7406');var s=bricks.getWidgetById('stats_row',bricks.app);if(s)s.render({});}else{if(at)at.set_text('\\u9519\\u8bef: '+(r.error||'\\u672a\\u77e5\\u9519\\u8bef'));}});"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
84
wwwroot/sd_dashboard/dashboard_data.dspy
Normal file
84
wwwroot/sd_dashboard/dashboard_data.dspy
Normal file
@ -0,0 +1,84 @@
|
||||
"""
|
||||
SDLC Dashboard Data API
|
||||
Returns real-time stats for the dashboard widgets
|
||||
"""
|
||||
from sqlor.dbpools import DBPools
|
||||
|
||||
async def main():
|
||||
dbname = get_module_dbname('pipeline-sdlc')
|
||||
|
||||
async with DBPools().sqlorContext(dbname) as sor:
|
||||
# 1) Project stats
|
||||
projects = await sor.sqlExe(
|
||||
"SELECT COUNT(*) as total, SUM(CASE WHEN status='active' THEN 1 ELSE 0 END) as active FROM sd_projects", {})
|
||||
p = projects[0] if projects else None
|
||||
|
||||
# 2) Active iterations
|
||||
iters = await sor.sqlExe(
|
||||
"SELECT COUNT(*) as cnt FROM sd_iterations WHERE status='in_progress'", {})
|
||||
active_iters = iters[0].cnt if iters else 0
|
||||
|
||||
# 3) Open bugs
|
||||
bugs = await sor.sqlExe(
|
||||
"SELECT COUNT(*) as cnt FROM sd_bugs WHERE status!='closed' AND status!='resolved'", {})
|
||||
open_bugs = bugs[0].cnt if bugs else 0
|
||||
|
||||
# 4) Pending reviews
|
||||
reviews = await sor.sqlExe(
|
||||
"SELECT COUNT(*) as cnt FROM pipeline_human_tasks WHERE status='pending' AND task_type='review'", {})
|
||||
pending_reviews = reviews[0].cnt if reviews else 0
|
||||
|
||||
# 5) Recent iterations with progress
|
||||
recent_iters = await sor.sqlExe("""
|
||||
SELECT i.name, i.status, i.start_date, i.end_date,
|
||||
COALESCE(tc.total,0) as total_tasks,
|
||||
COALESCE(tc.completed,0) as completed_tasks
|
||||
FROM sd_iterations i
|
||||
LEFT JOIN (
|
||||
SELECT iteration_id, COUNT(*) as total,
|
||||
SUM(CASE WHEN status='completed' THEN 1 ELSE 0 END) as completed
|
||||
FROM sd_test_plans GROUP BY iteration_id
|
||||
) tc ON tc.iteration_id = i.id
|
||||
WHERE i.status != 'closed'
|
||||
ORDER BY i.start_date DESC LIMIT 5
|
||||
""", {})
|
||||
|
||||
iter_list = []
|
||||
for r in recent_iters:
|
||||
pct = round(r.completed_tasks / r.total_tasks * 100) if r.total_tasks > 0 else 0
|
||||
iter_list.append({
|
||||
'name': r.name,
|
||||
'status': r.status,
|
||||
'progress': pct,
|
||||
'total': r.total_tasks,
|
||||
'completed': r.completed_tasks
|
||||
})
|
||||
|
||||
# 6) Bug by severity
|
||||
bug_by_sev = await sor.sqlExe("""
|
||||
SELECT COALESCE(severity,'unset') as severity, COUNT(*) as cnt
|
||||
FROM sd_bugs WHERE status!='closed'
|
||||
GROUP BY severity ORDER BY cnt DESC
|
||||
""", {})
|
||||
|
||||
severity_map = {
|
||||
'critical': {'label': '严重', 'color': '#FF4444'},
|
||||
'major': {'label': '主要', 'color': '#FF8800'},
|
||||
'minor': {'label': '次要', 'color': '#FFAA00'},
|
||||
'trivial': {'label': '轻微', 'color': '#888888'},
|
||||
'unset': {'label': '未分类', 'color': '#666666'}
|
||||
}
|
||||
bugs_data = []
|
||||
for r in bug_by_sev:
|
||||
sev = severity_map.get(r.severity, {'label': r.severity, 'color': '#888'})
|
||||
bugs_data.append({'label': sev['label'], 'count': r.cnt, 'color': sev['color']})
|
||||
|
||||
return {
|
||||
'projects_total': p.total if p else 0,
|
||||
'projects_active': p.active if p else 0,
|
||||
'active_iterations': active_iters,
|
||||
'open_bugs': open_bugs,
|
||||
'pending_reviews': pending_reviews,
|
||||
'recent_iterations': iter_list,
|
||||
'bugs_by_severity': bugs_data
|
||||
}
|
||||
@ -1,53 +1,53 @@
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"width": "100%", "height": "100%", "padding": "20px", "gap": "20px"},
|
||||
"options": {"width": "100%", "height": "100%", "padding": "24px", "gap": "24px"},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {"text": "📊 项目概览 Dashboard", "cfontsize": 24, "color": "#E0E0E0", "fontWeight": "bold"}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {"text": "软件开发生命周期全局统计", "cfontsize": 14, "color": "#888888"}
|
||||
"widgettype": "HBox",
|
||||
"options": {"alignItems": "center", "gap": "12px"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Title2", "options": {"text": "开发产线仪表盘"}},
|
||||
{"widgettype": "Text", "options": {"text": "项目/迭代/Bug实时概览", "cfontsize": 1.1, "color": "#888"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "ResponsableBox",
|
||||
"options": {"gap": "16px", "minWidth": "180px"},
|
||||
"options": {"gap": "16px", "minWidth": "200px"},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "8px", "border": "1px solid #2A2A3E", "cwidth": 25, "alignItems": "center"},
|
||||
"options": {"bgcolor": "#1e293b", "padding": "24px", "borderRadius": "12px", "gap": "8px", "border": "1px solid #334155", "cwidth": 25, "alignItems": "center"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "12", "cfontsize": 36, "color": "#4A90D9", "fontWeight": "bold"}},
|
||||
{"widgettype": "Text", "options": {"text": "活跃项目", "cfontsize": 14, "color": "#E0E0E0"}},
|
||||
{"widgettype": "Text", "options": {"text": "Active Projects", "cfontsize": 11, "color": "#888888"}}
|
||||
{"widgettype": "Text", "options": {"text": "-", "cfontsize": 2.5, "color": "#60a5fa", "fontWeight": "bold"}},
|
||||
{"widgettype": "Text", "options": {"text": "活跃项目", "cfontsize": 1, "color": "#e2e8f0"}},
|
||||
{"widgettype": "Text", "options": {"text": "Active Projects", "cfontsize": 0.8, "color": "#94a3b8"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "8px", "border": "1px solid #2A2A3E", "cwidth": 25, "alignItems": "center"},
|
||||
"options": {"bgcolor": "#1e293b", "padding": "24px", "borderRadius": "12px", "gap": "8px", "border": "1px solid #334155", "cwidth": 25, "alignItems": "center"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "5", "cfontsize": 36, "color": "#4AD97A", "fontWeight": "bold"}},
|
||||
{"widgettype": "Text", "options": {"text": "进行中迭代", "cfontsize": 14, "color": "#E0E0E0"}},
|
||||
{"widgettype": "Text", "options": {"text": "Active Iterations", "cfontsize": 11, "color": "#888888"}}
|
||||
{"widgettype": "Text", "options": {"text": "-", "cfontsize": 2.5, "color": "#4ade80", "fontWeight": "bold"}},
|
||||
{"widgettype": "Text", "options": {"text": "进行中迭代", "cfontsize": 1, "color": "#e2e8f0"}},
|
||||
{"widgettype": "Text", "options": {"text": "Active Sprints", "cfontsize": 0.8, "color": "#94a3b8"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "8px", "border": "1px solid #2A2A3E", "cwidth": 25, "alignItems": "center"},
|
||||
"options": {"bgcolor": "#1e293b", "padding": "24px", "borderRadius": "12px", "gap": "8px", "border": "1px solid #334155", "cwidth": 25, "alignItems": "center"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "23", "cfontsize": 36, "color": "#D9A04A", "fontWeight": "bold"}},
|
||||
{"widgettype": "Text", "options": {"text": "待处理Bug", "cfontsize": 14, "color": "#E0E0E0"}},
|
||||
{"widgettype": "Text", "options": {"text": "Open Bugs", "cfontsize": 11, "color": "#888888"}}
|
||||
{"widgettype": "Text", "options": {"text": "-", "cfontsize": 2.5, "color": "#fbbf24", "fontWeight": "bold"}},
|
||||
{"widgettype": "Text", "options": {"text": "待处理Bug", "cfontsize": 1, "color": "#e2e8f0"}},
|
||||
{"widgettype": "Text", "options": {"text": "Open Bugs", "cfontsize": 0.8, "color": "#94a3b8"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "8px", "border": "1px solid #2A2A3E", "cwidth": 25, "alignItems": "center"},
|
||||
"options": {"bgcolor": "#1e293b", "padding": "24px", "borderRadius": "12px", "gap": "8px", "border": "1px solid #334155", "cwidth": 25, "alignItems": "center"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "3", "cfontsize": 36, "color": "#9A4AD9", "fontWeight": "bold"}},
|
||||
{"widgettype": "Text", "options": {"text": "待审批", "cfontsize": 14, "color": "#E0E0E0"}},
|
||||
{"widgettype": "Text", "options": {"text": "Pending Reviews", "cfontsize": 11, "color": "#888888"}}
|
||||
{"widgettype": "Text", "options": {"text": "-", "cfontsize": 2.5, "color": "#a78bfa", "fontWeight": "bold"}},
|
||||
{"widgettype": "Text", "options": {"text": "待审批", "cfontsize": 1, "color": "#e2e8f0"}},
|
||||
{"widgettype": "Text", "options": {"text": "Pending Reviews", "cfontsize": 0.8, "color": "#94a3b8"}}
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -58,116 +58,18 @@
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "12px", "border": "1px solid #2A2A3E", "cwidth": 50},
|
||||
"options": {"bgcolor": "#1e293b", "padding": "24px", "borderRadius": "12px", "gap": "16px", "border": "1px solid #334155", "cwidth": 60},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "📈 迭代进度", "cfontsize": 16, "color": "#E0E0E0", "fontWeight": "bold"}},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"gap": "8px"},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {"gap": "8px", "alignItems": "center", "justifyContent": "space-between"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "Sprint 23 - API重构", "cfontsize": 13, "color": "#E0E0E0"}},
|
||||
{"widgettype": "Text", "options": {"text": "72%", "cfontsize": 13, "color": "#4AD97A", "fontWeight": "bold"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {"gap": "8px", "alignItems": "center", "justifyContent": "space-between"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "Sprint 23b - 测试覆盖", "cfontsize": 13, "color": "#E0E0E0"}},
|
||||
{"widgettype": "Text", "options": {"text": "45%", "cfontsize": 13, "color": "#D9A04A", "fontWeight": "bold"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {"gap": "8px", "alignItems": "center", "justifyContent": "space-between"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "Sprint 24 - 功能开发", "cfontsize": 13, "color": "#E0E0E0"}},
|
||||
{"widgettype": "Text", "options": {"text": "规划中", "cfontsize": 13, "color": "#4A90D9", "fontWeight": "bold"}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
{"widgettype": "Title3", "options": {"text": "迭代进度"}},
|
||||
{"widgettype": "Text", "options": {"text": "暂无数据,请先创建项目和迭代", "cfontsize": 0.9, "color": "#64748b", "padding": "20px 0"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "12px", "border": "1px solid #2A2A3E", "cwidth": 50},
|
||||
"options": {"bgcolor": "#1e293b", "padding": "24px", "borderRadius": "12px", "gap": "16px", "border": "1px solid #334155", "cwidth": 40},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "🐛 Bug趋势", "cfontsize": 16, "color": "#E0E0E0", "fontWeight": "bold"}},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"gap": "8px"},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {"gap": "8px", "alignItems": "center", "justifyContent": "space-between"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "严重 Critical", "cfontsize": 13, "color": "#E0E0E0"}},
|
||||
{"widgettype": "Text", "options": {"text": "3", "cfontsize": 13, "color": "#D94A4A", "fontWeight": "bold"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {"gap": "8px", "alignItems": "center", "justifyContent": "space-between"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "高 High", "cfontsize": 13, "color": "#E0E0E0"}},
|
||||
{"widgettype": "Text", "options": {"text": "8", "cfontsize": 13, "color": "#D9A04A", "fontWeight": "bold"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {"gap": "8px", "alignItems": "center", "justifyContent": "space-between"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "中 Medium", "cfontsize": 13, "color": "#E0E0E0"}},
|
||||
{"widgettype": "Text", "options": {"text": "12", "cfontsize": 13, "color": "#4A90D9", "fontWeight": "bold"}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"backgroundColor": "#1E1E2E", "padding": "20px", "borderRadius": "8px", "gap": "12px", "border": "1px solid #2A2A3E"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "🚀 最近部署 Recent Deployments", "cfontsize": 16, "color": "#E0E0E0", "fontWeight": "bold"}},
|
||||
{
|
||||
"widgettype": "ResponsableBox",
|
||||
"options": {"gap": "12px", "minWidth": "250px"},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"backgroundColor": "#121212", "padding": "12px", "borderRadius": "6px", "gap": "4px", "cwidth": 33},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "✅ Production", "cfontsize": 13, "color": "#4AD97A", "fontWeight": "bold"}},
|
||||
{"widgettype": "Text", "options": {"text": "v2.2.5 → v2.2.6", "cfontsize": 12, "color": "#E0E0E0"}},
|
||||
{"widgettype": "Text", "options": {"text": "2026-06-15 18:30", "cfontsize": 11, "color": "#888888"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"backgroundColor": "#121212", "padding": "12px", "borderRadius": "6px", "gap": "4px", "cwidth": 33},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "🔄 Staging", "cfontsize": 13, "color": "#D9A04A", "fontWeight": "bold"}},
|
||||
{"widgettype": "Text", "options": {"text": "v2.3.0-rc1", "cfontsize": 12, "color": "#E0E0E0"}},
|
||||
{"widgettype": "Text", "options": {"text": "2026-06-16 10:00", "cfontsize": 11, "color": "#888888"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"backgroundColor": "#121212", "padding": "12px", "borderRadius": "6px", "gap": "4px", "cwidth": 33},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": "🧪 Dev", "cfontsize": 13, "color": "#4A90D9", "fontWeight": "bold"}},
|
||||
{"widgettype": "Text", "options": {"text": "v2.3.0-dev.47", "cfontsize": 12, "color": "#E0E0E0"}},
|
||||
{"widgettype": "Text", "options": {"text": "2026-06-16 14:22", "cfontsize": 11, "color": "#888888"}}
|
||||
]
|
||||
}
|
||||
{"widgettype": "Title3", "options": {"text": "Bug 分布"}},
|
||||
{"widgettype": "Text", "options": {"text": "暂无Bug数据", "cfontsize": 0.9, "color": "#64748b", "padding": "20px 0"}}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@ -54,14 +54,14 @@
|
||||
"env_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_env_type.dspy')}}",
|
||||
"valueField": "env_type",
|
||||
"textField": "env_type_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_env_status.dspy')}}",
|
||||
"valueField": "status",
|
||||
"textField": "status_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -97,7 +97,7 @@
|
||||
"valueField": "project_id",
|
||||
"textField": "project_id_text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "sd_projects",
|
||||
"tblvalue": "id",
|
||||
"tbltext": "name",
|
||||
@ -114,15 +114,15 @@
|
||||
"nullable": "no",
|
||||
"label": "环境类型",
|
||||
"uitype": "code",
|
||||
"valueField": "env_type",
|
||||
"textField": "env_type_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "env_type",
|
||||
"textField": "env_type_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_env_type'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_env_type.dspy')}}"
|
||||
@ -262,15 +262,15 @@
|
||||
"default": "'configured'",
|
||||
"label": "环境状态",
|
||||
"uitype": "code",
|
||||
"valueField": "status",
|
||||
"textField": "status_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "status",
|
||||
"textField": "status_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_env_status'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_env_status.dspy')}}"
|
||||
|
||||
@ -70,14 +70,14 @@
|
||||
"iteration_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_iteration_type.dspy')}}",
|
||||
"valueField": "iteration_type",
|
||||
"textField": "iteration_type_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_iteration_status.dspy')}}",
|
||||
"valueField": "status",
|
||||
"textField": "status_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -115,7 +115,7 @@
|
||||
"valueField": "project_id",
|
||||
"textField": "project_id_text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "sd_projects",
|
||||
"tblvalue": "id",
|
||||
"tbltext": "name",
|
||||
@ -144,15 +144,15 @@
|
||||
"default": "'new_feature'",
|
||||
"label": "迭代类型",
|
||||
"uitype": "code",
|
||||
"valueField": "iteration_type",
|
||||
"textField": "iteration_type_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "iteration_type",
|
||||
"textField": "iteration_type_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_iteration_type'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_iteration_type.dspy')}}"
|
||||
@ -185,15 +185,15 @@
|
||||
"default": "'planning'",
|
||||
"label": "迭代状态",
|
||||
"uitype": "code",
|
||||
"valueField": "status",
|
||||
"textField": "status_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "status",
|
||||
"textField": "status_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_iteration_status'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_iteration_status.dspy')}}"
|
||||
|
||||
@ -23,6 +23,7 @@ if not userorgid:
|
||||
}
|
||||
}
|
||||
ns['org_id'] = userorgid
|
||||
ns['created_by'] = userorgid
|
||||
|
||||
db = DBPools()
|
||||
dbname = get_module_dbname('pipeline_sdlc')
|
||||
|
||||
@ -58,19 +58,26 @@
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_status.dspy')}}",
|
||||
"valueField": "status",
|
||||
"textField": "status_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"project_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_project_type.dspy')}}",
|
||||
"valueField": "project_type",
|
||||
"textField": "project_type_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
,
|
||||
"pipeline_id": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_pipeline_options.dspy')}}",
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
}
|
||||
"editexclouded":[
|
||||
"id",
|
||||
"created_at",
|
||||
@ -119,15 +126,15 @@
|
||||
"default": "'web_app'",
|
||||
"label": "项目类型",
|
||||
"uitype": "code",
|
||||
"valueField": "project_type",
|
||||
"textField": "project_type_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "project_type",
|
||||
"textField": "project_type_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_project_type'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_project_type.dspy')}}"
|
||||
@ -170,15 +177,15 @@
|
||||
"default": "'draft'",
|
||||
"label": "项目状态",
|
||||
"uitype": "code",
|
||||
"valueField": "status",
|
||||
"textField": "status_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "status",
|
||||
"textField": "status_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_project_status'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_status.dspy')}}"
|
||||
|
||||
@ -18,6 +18,7 @@ if not userorgid:
|
||||
}
|
||||
}
|
||||
ns['org_id'] = userorgid
|
||||
ns['created_by'] = userorgid
|
||||
|
||||
|
||||
|
||||
|
||||
@ -50,20 +50,20 @@
|
||||
"case_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_case_type.dspy')}}",
|
||||
"valueField": "case_type",
|
||||
"textField": "case_type_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_case_status.dspy')}}",
|
||||
"valueField": "status",
|
||||
"textField": "status_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"priority": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_priority.dspy')}}",
|
||||
"valueField": "priority",
|
||||
"textField": "priority_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -101,7 +101,7 @@
|
||||
"valueField": "plan_id",
|
||||
"textField": "plan_id_text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "sd_test_plans",
|
||||
"tblvalue": "id",
|
||||
"tbltext": "plan_name",
|
||||
@ -129,15 +129,15 @@
|
||||
"nullable": "no",
|
||||
"label": "用例类型",
|
||||
"uitype": "code",
|
||||
"valueField": "case_type",
|
||||
"textField": "case_type_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "case_type",
|
||||
"textField": "case_type_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_test_case_type'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_case_type.dspy')}}"
|
||||
@ -151,15 +151,15 @@
|
||||
"default": "'P2'",
|
||||
"label": "优先级",
|
||||
"uitype": "code",
|
||||
"valueField": "priority",
|
||||
"textField": "priority_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "priority",
|
||||
"textField": "priority_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_priority'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_priority.dspy')}}"
|
||||
@ -209,15 +209,15 @@
|
||||
"default": "'pending'",
|
||||
"label": "用例状态",
|
||||
"uitype": "code",
|
||||
"valueField": "status",
|
||||
"textField": "status_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "status",
|
||||
"textField": "status_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_test_case_status'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_case_status.dspy')}}"
|
||||
|
||||
@ -61,14 +61,14 @@
|
||||
"plan_type": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_plan_type.dspy')}}",
|
||||
"valueField": "plan_type",
|
||||
"textField": "plan_type_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
},
|
||||
"status": {
|
||||
"uitype": "code",
|
||||
"dataurl": "{{entire_url('../api/get_search_plan_status.dspy')}}",
|
||||
"valueField": "status",
|
||||
"textField": "status_text"
|
||||
"valueField": "value",
|
||||
"textField": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -103,7 +103,7 @@
|
||||
"valueField": "iteration_id",
|
||||
"textField": "iteration_id_text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "sd_iterations",
|
||||
"tblvalue": "id",
|
||||
"tbltext": "iteration_name",
|
||||
@ -131,15 +131,15 @@
|
||||
"nullable": "no",
|
||||
"label": "方案类型",
|
||||
"uitype": "code",
|
||||
"valueField": "plan_type",
|
||||
"textField": "plan_type_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "plan_type",
|
||||
"textField": "plan_type_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_test_plan_type'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_plan_type.dspy')}}"
|
||||
@ -189,15 +189,15 @@
|
||||
"default": "'draft'",
|
||||
"label": "方案状态",
|
||||
"uitype": "code",
|
||||
"valueField": "status",
|
||||
"textField": "status_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"params": {
|
||||
"dbname": "{{get_module_dbname('pipeline_sdlc')}}",
|
||||
"dbname": "{{'sage'}}",
|
||||
"table": "appcodes_kv",
|
||||
"tblvalue": "k",
|
||||
"tbltext": "v",
|
||||
"valueField": "status",
|
||||
"textField": "status_text",
|
||||
"valueField": "value",
|
||||
"textField": "text",
|
||||
"cond": "parentid='sd_test_plan_status'"
|
||||
},
|
||||
"dataurl": "{{entire_url('../api/get_search_plan_status.dspy')}}"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user