Skip to content

Commit c779b22

Browse files
committed
feat: Add dynamic options support for block configurations and LLM model selection
1 parent cbc31f4 commit c779b22

File tree

7 files changed

+80
-22
lines changed

7 files changed

+80
-22
lines changed

framework/llm/llm_manager.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,11 @@ def get_llm(self, model_id: str) -> Optional[LLMBackendAdapter]:
145145
return None
146146
# TODO: 后续考虑支持更多的选择策略
147147
return random.choice(backends)
148-
149-
def get_llm_id_by_ability(self, ability: LLMAbility) -> str:
148+
149+
def get_supported_models(self, ability: LLMAbility) -> List[str]:
150150
"""
151-
根据指定的能力获取严格符合要求的 LLM 适配器列表。
152-
:param ability: 指定的能力。
153-
:return: 符合要求的 LLM 适配器列表。
151+
获取所有支持的模型
152+
:return: 支持的模型列表
154153
"""
155154
adapter_types = self.backend_registry.search_adapter_by_ability(ability)
156155
matched_backends = set()
@@ -162,5 +161,16 @@ def get_llm_id_by_ability(self, ability: LLMAbility) -> str:
162161
break
163162

164163
if not matched_backends:
164+
return []
165+
return list(matched_backends)
166+
167+
def get_llm_id_by_ability(self, ability: LLMAbility) -> str:
168+
"""
169+
根据指定的能力获取严格符合要求的 LLM 适配器列表。
170+
:param ability: 指定的能力。
171+
:return: 符合要求的 LLM 适配器列表。
172+
"""
173+
supported_models = self.get_supported_models(ability)
174+
if not supported_models:
165175
return None
166-
return random.choice(list(matched_backends))
176+
return random.choice(supported_models)

framework/web/api/block/routes.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,21 @@ async def list_block_types():
1818
for block_type in registry.get_all_types():
1919
inputs, outputs, configs = registry.extract_block_info(block_type)
2020
type_name = registry.get_block_type_name(block_type)
21-
types.append(
22-
BlockType(
23-
type_name=type_name,
21+
22+
for config in configs.values():
23+
if config.has_options:
24+
config.options = config.options_provider(g.container, block_type)
25+
26+
block_type_info = BlockType(
27+
type_name=type_name,
2428
name=block_type.name,
2529
label=registry.get_localized_name(type_name),
2630
description=getattr(block_type, "description", ""),
2731
inputs=inputs.values(),
2832
outputs=outputs.values(),
2933
configs=configs.values(),
30-
)
3134
)
35+
types.append(block_type_info)
3236

3337
return BlockTypeList(types=types).model_dump()
3438

@@ -69,6 +73,8 @@ async def get_block_type(type_name: str):
6973

7074
configs = []
7175
for name, info in block_type.get_configs().items():
76+
if info.has_options:
77+
info.options = info.options_provider(g.container, block_type)
7278
configs.append(
7379
BlockConfig(
7480
name=name,
Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1-
from typing import Optional
1+
from typing import Callable, List, Optional, TypeVar
22

3+
from framework.ioc.container import DependencyContainer
4+
from framework.workflow.core.block import Block
5+
6+
T = TypeVar("T")
7+
OptionsProvider = Callable[[DependencyContainer, Block], List[T]]
38

49
class ParamMeta:
5-
def __init__(self, label: Optional[str] = None, description: Optional[str] = None):
10+
def __init__(self, label: Optional[str] = None, description: Optional[str] = None, options_provider: Optional[OptionsProvider[T]] = None):
611
self.label = label
712
self.description = description
13+
self.options_provider = options_provider
814

915
def __repr__(self):
10-
return f"ParamMeta(label={self.label}, description={self.description})"
16+
return f"ParamMeta(label={self.label}, description={self.description}, options_provider={self.options_provider})"
1117

1218
def __str__(self):
1319
return self.__repr__()
20+
21+
def get_options(self, block: Block) -> List[T]:
22+
if self.options_provider:
23+
return self.options_provider(block)
24+
return []
25+

framework/workflow/core/block/registry.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ def extract_block_param(param: Parameter, type_system: TypeSystem) -> BlockConfi
1616
param_type = param.annotation
1717
label = param.name
1818
description = None
19-
19+
has_options = False
20+
options_provider = None
2021
if get_origin(param_type) is Annotated:
2122
args = get_args(param_type)
2223
if len(args) > 0:
@@ -26,6 +27,8 @@ def extract_block_param(param: Parameter, type_system: TypeSystem) -> BlockConfi
2627
if isinstance(metadata, ParamMeta):
2728
label = metadata.label
2829
description = metadata.description
30+
has_options = metadata.options_provider is not None
31+
options_provider = metadata.options_provider
2932

3033
# 递归调用 extract_block_param 处理实际类型
3134
block_config = extract_block_param(
@@ -54,6 +57,9 @@ def extract_block_param(param: Parameter, type_system: TypeSystem) -> BlockConfi
5457
required=required,
5558
default=default,
5659
label=label,
60+
has_options=has_options,
61+
options=[],
62+
options_provider=options_provider,
5763
)
5864

5965

@@ -145,7 +151,7 @@ def extract_block_info(
145151
for name, input_info in getattr(block_type, "inputs", {}).items():
146152
type_name = self._type_system.get_type_name(input_info.data_type)
147153
self._type_system.register_type(type_name, input_info.data_type)
148-
154+
149155
inputs[name] = BlockInput(
150156
name=name,
151157
label=input_info.label,
@@ -158,7 +164,7 @@ def extract_block_info(
158164
for name, output_info in getattr(block_type, "outputs", {}).items():
159165
type_name = self._type_system.get_type_name(output_info.data_type)
160166
self._type_system.register_type(type_name, output_info.data_type)
161-
167+
162168
outputs[name] = BlockOutput(
163169
name=name,
164170
label=output_info.label,

framework/workflow/core/block/schema.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
from typing import Any, Optional
1+
from typing import Any, List, Optional
22

3-
from pydantic import BaseModel
3+
from pydantic import BaseModel, Field
4+
5+
from framework.workflow.core.block.param import OptionsProvider
46

57

68
class BlockInput(BaseModel):
@@ -32,3 +34,6 @@ class BlockConfig(BaseModel):
3234
required: bool = True
3335
default: Optional[Any] = None
3436
label: Optional[str] = None
37+
has_options: bool = False
38+
options: Optional[List[Any]] = None
39+
options_provider: Optional[OptionsProvider] = Field(exclude=True)

framework/workflow/implementations/blocks/llm/chat.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
from framework.workflow.core.execution.executor import WorkflowExecutor
1515

1616

17+
def model_name_options_provider(container: DependencyContainer, block: Block) -> List[str]:
18+
llm_manager: LLMManager = container.resolve(LLMManager)
19+
return llm_manager.get_supported_models(LLMAbility.TextChat)
20+
1721
class ChatMessageConstructor(Block):
1822
name = "chat_message_constructor"
1923
inputs = {
@@ -124,7 +128,8 @@ class ChatCompletion(Block):
124128
def __init__(
125129
self,
126130
model_name: Annotated[
127-
Optional[str], ParamMeta(label="模型 ID", description="要使用的模型 ID")
131+
Optional[str],
132+
ParamMeta(label="模型 ID", description="要使用的模型 ID", options_provider=model_name_options_provider),
128133
] = None,
129134
):
130135
self.model_name = model_name

framework/workflow/implementations/blocks/memory/chat_memory.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Annotated, Any, Dict, Optional
1+
from typing import Annotated, Any, Dict, List, Optional
22

33
from framework.im.message import IMMessage
44
from framework.im.sender import ChatSender
@@ -10,6 +10,10 @@
1010
from framework.workflow.core.block import Block, Input, Output, ParamMeta
1111

1212

13+
def scope_type_options_provider(container: DependencyContainer, block: Block) -> List[str]:
14+
return ["global", "user", "group"]
15+
16+
1317
class ChatMemoryQuery(Block):
1418
name = "chat_memory_query"
1519
inputs = {
@@ -23,7 +27,12 @@ class ChatMemoryQuery(Block):
2327
def __init__(
2428
self,
2529
scope_type: Annotated[
26-
Optional[str], ParamMeta(label="级别", description="要查询记忆的级别")
30+
Optional[str],
31+
ParamMeta(
32+
label="级别",
33+
description="要查询记忆的级别",
34+
options_provider=scope_type_options_provider,
35+
),
2736
],
2837
):
2938
self.scope_type = scope_type
@@ -63,7 +72,12 @@ class ChatMemoryStore(Block):
6372
def __init__(
6473
self,
6574
scope_type: Annotated[
66-
Optional[str], ParamMeta(label="级别", description="要查询记忆的级别")
75+
Optional[str],
76+
ParamMeta(
77+
label="级别",
78+
description="要查询记忆的级别",
79+
options_provider=scope_type_options_provider,
80+
),
6781
],
6882
):
6983
self.scope_type = scope_type

0 commit comments

Comments
 (0)