Skip to content

Commit f71ff84

Browse files
authored
Merge pull request #71 from OpenDCAI/dev
[backend] update to fix prompt config to apply StrFormatOperator, update logo, update kwarg_params for OP
2 parents 1402dba + ec087b2 commit f71ff84

18 files changed

Lines changed: 516 additions & 79 deletions

File tree

backend/app/api/v1/endpoints/prompts.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
from fastapi import APIRouter, HTTPException
3-
from app.schemas.prompt import GetPromptSchema, PromptSourceOut, OperatorPromptMapOut, PromptInfoMapOut
3+
from app.schemas.prompt import GetPromptSchema, PromptSourceOut, OperatorPromptMapOut, PromptInfoMapOut, PromptInfoOut
44
# from app.services.prompt_registry import _PROMPT_REGISTRY
55
from app.core.container import container
66
from app.api.v1.resp import ok
@@ -25,6 +25,14 @@ def get_operator_prompt_mapping():
2525
def get_prompt_info():
2626
return ok(container.prompt_registry.list_prompt_info())
2727

28+
@router.get(
29+
"/prompt-info/{prompt_name}",
30+
response_model=ApiResponse[PromptInfoOut],
31+
summary="根据 Prompt 名称获取 Prompt 信息"
32+
)
33+
def get_prompt_info(prompt_name: str):
34+
return ok(container.prompt_registry.list_prompt_info().prompts[prompt_name])
35+
2836
@router.get(
2937
"/{operator_name}",
3038
response_model=ApiResponse[GetPromptSchema],

backend/app/core/config.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,24 @@ class Settings(BaseSettings):
2929
PREFERENCES_PATH: str = os.path.join(BASE_DIR, "data", "user_preferences.json") # preference information cache
3030
SQLITE_DB_DIR: str = os.path.join(BASE_DIR, "data", "text2sql_dbs") # where sqlite database files are stored
3131
CACHE_DIR: str = os.path.join(BASE_DIR, "cache_local") # cache directory for pipeline execution
32-
DEFAULT_SERVING_FILLING: bool = False
32+
DEFAULT_SERVING_FILLING: bool = True # whether to fill default values for missing fields in serving
33+
34+
# white list of preset pipelines that can be shown in the frontend pipeline template list
35+
_PRESET_PIPELINE_NAME_WHITELIST = {
36+
"Agentic Rag Pipeline",
37+
"Chemistry Smiles",
38+
"Code Code To Sft Data Pipeline",
39+
"Code Gen Dataset Pipeline",
40+
"Func Call Synthesis",
41+
"Reasoning General Pipeline",
42+
"Reasoning Math Pipeline",
43+
"Reasoning Pretrain Pipeline",
44+
"Text Conversation Synthesis Pipeline",
45+
"Text Sft Synthesis Pipeline",
46+
"Text2Qa Pipeline",
47+
"Text2Sql Pipeline Refine",
48+
"Text2Sql Pipeline Gen",
49+
"Double Column Input"
50+
}
3351

3452
settings = Settings()

backend/app/schemas/prompt.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from pydantic import BaseModel, Field
2-
from typing import Optional, List, Dict
2+
from typing import Any, Optional, List, Dict
33

44
class GetPromptSchema(BaseModel):
55
#allowed_prompts: Optional[List[str]] = []
@@ -12,11 +12,24 @@ class PromptSourceOut(BaseModel):
1212
class OperatorPromptMapOut(BaseModel):
1313
operator_prompts: Dict[str, List[str]]
1414

15+
# 同OperatorDetailSchema,定义一个 Prompt 的详细信息
16+
class PromptParameterSchema(BaseModel):
17+
name: str
18+
default_value: Any # 默认值可以是任何类型,所以用 Any
19+
kind: str # 例如 "POSITIONAL_OR_KEYWORD"
20+
21+
class PromptParameterGroupsSchema(BaseModel):
22+
init: List[PromptParameterSchema]
23+
build_prompt: List[PromptParameterSchema]
24+
25+
1526
class PromptInfoOut(BaseModel):
1627
operator: List[str]
1728
class_str: str
1829
primary_type: str
1930
secondary_type: str
31+
description: str
32+
parameter: PromptParameterGroupsSchema
2033

2134
class PromptInfoMapOut(BaseModel):
2235
prompts: Dict[str, PromptInfoOut]

backend/app/services/pipeline_registry.py

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -456,21 +456,6 @@ def _update_all_api_pipelines_operators(self):
456456
except Exception as e:
457457
logger.error(f"Error updating API pipeline operators: {e}", exc_info=True)
458458

459-
_PRESET_PIPELINE_NAME_WHITELIST = {
460-
"Agentic Rag Pipeline",
461-
"Chemistry Smiles",
462-
"Code Code To Sft Data Pipeline",
463-
"Code Gen Dataset Pipeline",
464-
"Func Call Synthesis",
465-
"Reasoning General Pipeline",
466-
"Reasoning Math Pipeline",
467-
"Reasoning Pretrain Pipeline",
468-
"Text Conversation Synthesis Pipeline",
469-
"Text Sft Synthesis Pipeline",
470-
"Text2Qa Pipeline",
471-
"Text2Sql Pipeline Refine",
472-
"Text2Sql Pipeline Gen",
473-
}
474459

475460
def _normalize_pipeline_name(self, name: str) -> str:
476461
return re.sub(r"\s+", " ", (name or "")).strip()
@@ -491,9 +476,9 @@ def _is_preset_pipeline(self, pipeline: Dict[str, Any]) -> bool:
491476
return False
492477

493478
def _is_visible_pipeline(self, pipeline: Dict[str, Any]) -> bool:
494-
if self._is_preset_pipeline(pipeline):
479+
if self._is_preset_pipeline(pipeline):
495480
name = self._normalize_pipeline_name(pipeline.get("name", ""))
496-
return name in self._PRESET_PIPELINE_NAME_WHITELIST
481+
return name in settings._PRESET_PIPELINE_NAME_WHITELIST
497482
return True
498483

499484
def list_templates(self) -> List[Dict[str, Any]]:
@@ -667,6 +652,11 @@ def _update_pipeline_op_info(self, new_op_info: Dict[str, Any], old_op_info: Dic
667652
param["value"] = param_value
668653
elif param.get('value') is None:
669654
param["value"] = param.get("default_value", None)
655+
656+
old_run_params_name_list = [param.get("name") for param in old_params.get("run", [])]
657+
for param_name, param_value in new_run_params.items():
658+
if param_name is not None and param_name not in old_run_params_name_list:
659+
old_params.get("run", []).append({"name": param_name, "value": param_value})
670660

671661
return old_op_info
672662

backend/app/services/prompt_registry.py

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,49 @@
99

1010
logger = get_logger(__name__)
1111

12+
import json
13+
import inspect
14+
from typing import Any, Dict, List
15+
16+
def _safe_json_val(val: Any) -> Any:
17+
if val is inspect.Parameter.empty:
18+
return None
19+
if isinstance(val, type) or callable(val):
20+
return str(val)
21+
try:
22+
json.dumps(val)
23+
return val
24+
except TypeError:
25+
return str(val)
26+
27+
def _param_to_dict(p: inspect.Parameter) -> Dict[str, Any]:
28+
return {
29+
"name": p.name,
30+
"default_value": _safe_json_val(p.default),
31+
"kind": p.kind.name,
32+
}
33+
34+
def _get_method_params(method: Any, skip_first_self: bool = False) -> List[Dict[str, Any]]:
35+
try:
36+
if method is None:
37+
return []
38+
sig = inspect.signature(method)
39+
params = list(sig.parameters.values())
40+
if skip_first_self and params and params[0].name == "self":
41+
params = params[1:]
42+
return [_param_to_dict(p) for p in params]
43+
except Exception:
44+
return []
45+
46+
def _get_docstring(obj) -> str:
47+
"""
48+
Return cleaned docstring (dedented), fallback to ''.
49+
"""
50+
try:
51+
return inspect.getdoc(obj) or ""
52+
except Exception:
53+
return ""
54+
1255

1356
class PromptRegistry:
1457
"""
@@ -80,48 +123,48 @@ def list_operator_prompts(self) -> OperatorPromptMapOut:
80123
return OperatorPromptMapOut(operator_prompts=result)
81124

82125
def list_prompt_info(self) -> PromptInfoMapOut:
83-
84126
prompt_map = self._prompt_registry.get_obj_map()
85127
operator_map = self._operator_registry.get_obj_map()
86128

87-
# ---- 第一部分:构建 Prompt → Operators 列表 ----
129+
# Prompt → Operators
88130
prompt_to_ops: Dict[str, List[str]] = {}
89-
90131
for op_name, op_cls in operator_map.items():
91132
allowed = getattr(op_cls, "ALLOWED_PROMPTS", [])
92133
for p_cls in allowed:
93-
p_name = p_cls.__name__
94-
prompt_to_ops.setdefault(p_name, []).append(op_name)
134+
prompt_to_ops.setdefault(p_cls.__name__, []).append(op_name)
95135

96-
# ---- 第二部分:获取 Prompt 的分类信息 ----
97136
prompt_types = self._prompt_registry.get_type_of_objects()
98-
99-
# ---- 第三部分:构造最终结构 ----
100137
result: Dict[str, PromptInfoOut] = {}
101138

102139
for p_name, p_cls in prompt_map.items():
103140
if not isclass(p_cls):
104141
continue
105142

106-
# operator list
107143
ops = prompt_to_ops.get(p_name, [])
108-
109-
# class path string
110144
class_str = f"{p_cls.__module__}.{p_cls.__name__}"
111145

112-
# categories
113146
type_path = prompt_types.get(p_name, [])
114147
primary_type = type_path[0] if len(type_path) > 0 else "Unknown"
115148
secondary_type = type_path[1] if len(type_path) > 1 else "Unknown"
116149

150+
151+
# ✅ docstring
152+
description = _get_docstring(p_cls)
153+
# ✅ 新增:init 参数
154+
init_params = _get_method_params(getattr(p_cls, "__init__", None), skip_first_self=True)
155+
build_params = _get_method_params(getattr(p_cls, "build_prompt", None), skip_first_self=True)
156+
117157
result[p_name] = PromptInfoOut(
118158
operator=ops,
119159
class_str=class_str,
120160
primary_type=primary_type,
121-
secondary_type=secondary_type
161+
secondary_type=secondary_type,
162+
description=description,
163+
parameter={"init": init_params, "build_prompt": build_params}, # ✅ 新字段
122164
)
123165

124166
return PromptInfoMapOut(prompts=result)
125167

168+
126169
# 全局单例
127170
# _PROMPT_REGISTRY = PromptRegistry()

backend/app/services/ray_pipeline_executor.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ def add_log(stage: str, message: str, op_name: str = "__pipeline__"):
267267
for param in op.get("params", {}).get("init", []):
268268
param_name = param.get("name")
269269
param_value = param.get("value")
270-
270+
if isinstance(param_value, str) and param_value == "":
271+
param_value = None
271272
try:
272273
if param_name == "llm_serving":
273274
serving_id = param_value
@@ -383,15 +384,28 @@ def add_log(stage: str, message: str, op_name: str = "__pipeline__"):
383384
param_value = db_manager_instance_map[db_manager_id]
384385

385386
elif param_name == "prompt_template":
386-
prompt_cls_name = extract_class_name(param_value)
387-
add_log("init", f"[{datetime.now().isoformat()}] - Loading prompt template: {prompt_cls_name}", op_key)
388-
prompt_cls = PROMPT_REGISTRY.get(prompt_cls_name)
389-
if not prompt_cls:
390-
raise DataFlowEngineError(
391-
f"Prompt class not found: {prompt_cls_name}",
392-
context={"operator": op_name, "param": param_name}
393-
)
394-
param_value = prompt_cls()
387+
if isinstance(param_value, str):
388+
prompt_cls_name = extract_class_name(param_value)
389+
add_log("init", f"[{datetime.now().isoformat()}] - Loading prompt template: {prompt_cls_name}", op_key)
390+
prompt_cls = PROMPT_REGISTRY.get(prompt_cls_name)
391+
if not prompt_cls:
392+
raise DataFlowEngineError(
393+
f"Prompt class not found: {prompt_cls_name}",
394+
context={"operator": op_name, "param": param_name}
395+
)
396+
param_value = prompt_cls()
397+
elif isinstance(param_value, dict):
398+
prompt_cls_name = extract_class_name(param_value.get("cls_name"))
399+
prompt_cls = PROMPT_REGISTRY.get(prompt_cls_name)
400+
if not prompt_cls:
401+
raise DataFlowEngineError(
402+
f"Prompt class not found: {prompt_cls_name}",
403+
context={"operator": op_name, "param": param_name}
404+
)
405+
param_dict = {}
406+
for param in param_value.get("params", []):
407+
param_dict[param.get("name")] = param.get("value") if param.get("value") is not None else param.get("default_value")
408+
param_value = prompt_cls(**param_dict)
395409

396410
init_params[param_name] = param_value
397411

@@ -413,7 +427,11 @@ def add_log(stage: str, message: str, op_name: str = "__pipeline__"):
413427
for param in op.get("params", {}).get("run", []):
414428
param_name = param.get("name")
415429
param_value = param.get("value")
416-
run_params[param_name] = param_value
430+
if param.get('kind') == "VAR_KEYWORD":
431+
for item in param_value:
432+
run_params[item.get("name")] = item.get("value") if item.get("value") is not None else item.get("default_value")
433+
else:
434+
run_params[param_name] = param_value
417435

418436
# 实例化 Operator
419437
operator_cls_name = extract_class_name(op_name)

frontend/public/favicon.ico

144 KB
Binary file not shown.

frontend/src/assets/logo/logo.ico

148 KB
Binary file not shown.

frontend/src/axios/api.js

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,7 @@ export class tasks {
998998
}
999999

10001000
/**
1001-
* @summary 下载任务执行结果文件
1001+
* @summary 下载任务执行结果文件,step从0开始计数,想请求第一个算子传step=0
10021002
* @param {String} [pathtask_id]
10031003
* @param {Number} [step]
10041004
* @param {CancelTokenSource} [cancelSource] Axios Cancel Source 对象,可以取消该请求
@@ -1729,6 +1729,57 @@ export class prompts {
17291729
})
17301730
}
17311731

1732+
/**
1733+
* @summary 根据 Prompt 名称获取 Prompt 信息
1734+
* @param {String} [pathprompt_name]
1735+
* @param {CancelTokenSource} [cancelSource] Axios Cancel Source 对象,可以取消该请求
1736+
* @param {Function} [uploadProgress] 上传回调函数
1737+
* @param {Function} [downloadProgress] 下载回调函数
1738+
*/
1739+
static async get_prompt_info_api_v1_prompts_prompt_info__prompt_name__get(pathprompt_name,cancelSource,uploadProgress,downloadProgress){
1740+
return await new Promise((resolve,reject)=>{
1741+
let responseType = "json";
1742+
let options = {
1743+
method:'get',
1744+
url:'/api/v1/prompts/prompt-info/'+pathprompt_name+'',
1745+
data:{},
1746+
params:{},
1747+
headers:{
1748+
"Content-Type":""
1749+
},
1750+
onUploadProgress:uploadProgress,
1751+
onDownloadProgress:downloadProgress
1752+
}
1753+
// support wechat mini program
1754+
if (cancelSource!=undefined){
1755+
options.cancelToken = cancelSource.token
1756+
}
1757+
if (responseType != "json"){
1758+
options.responseType = responseType;
1759+
}
1760+
axios(options)
1761+
.then(res=>{
1762+
if (res.config.responseType=="blob"){
1763+
resolve(new Blob([res.data],{
1764+
type: res.headers["content-type"].split(";")[0]
1765+
}))
1766+
}else{
1767+
resolve(res.data);
1768+
return res.data
1769+
}
1770+
}).catch(err=>{
1771+
if (err.response){
1772+
if (err.response.data)
1773+
reject(err.response.data)
1774+
else
1775+
reject(err.response);
1776+
}else{
1777+
reject(err)
1778+
}
1779+
})
1780+
})
1781+
}
1782+
17321783
/**
17331784
* @summary 根据算子名称获取对应的 Prompt 列表
17341785
* @param {String} [pathoperator_name]
@@ -1850,6 +1901,14 @@ prompts.get_prompt_info_api_v1_prompts_prompt_info_get.fullPath=`${axios.default
18501901
*/
18511902
prompts.get_prompt_info_api_v1_prompts_prompt_info_get.path=`/api/v1/prompts/prompt-info`
18521903
/**
1904+
* @description get_prompt_info_api_v1_prompts_prompt_info__prompt_name__get url链接,包含baseURL
1905+
*/
1906+
prompts.get_prompt_info_api_v1_prompts_prompt_info__prompt_name__get.fullPath=`${axios.defaults.baseURL}/api/v1/prompts/prompt-info/{prompt_name}`
1907+
/**
1908+
* @description get_prompt_info_api_v1_prompts_prompt_info__prompt_name__get url链接,不包含baseURL
1909+
*/
1910+
prompts.get_prompt_info_api_v1_prompts_prompt_info__prompt_name__get.path=`/api/v1/prompts/prompt-info/{prompt_name}`
1911+
/**
18531912
* @description get_prompts_api_v1_prompts__operator_name__get url链接,包含baseURL
18541913
*/
18551914
prompts.get_prompts_api_v1_prompts__operator_name__get.fullPath=`${axios.defaults.baseURL}/api/v1/prompts/{operator_name}`

0 commit comments

Comments
 (0)