Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions doc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## Unreleased

Converter 定义精简(不涉及 Operator ABI、Pipeline JSON 与 `.conf`):删除没有运行时作用的
`schema_version`、`external_type`、输出 `cardinality` 与 `capacity_policy`,以及槽位的 `value_type` 与
`capacity_fields`。输出槽容量字段只由 ValueType 决定,`ExternalInputSlot` / `ExternalOutputSlot`
只接受槽名。Catalog 的 Converter 不再导出上述元数据,`external_type` 改由槽位类型推导。
`schema_id` 保留为外部协议 ID:载体相同的 Converter 可能解析语义不同,同业务 binding 的一致性检查
继续比较它。

开发工具修复:Markdown 链接检查支持单引号与圆括号标题、带空格的尖括号目标及平衡或转义的
目标圆括号;生产版 `alg_pipeline_tool edit` 根据 `validation.diagnostics` 提示构建变体与测试工具。
交付脚本要求远端 PR head 与已验证提交一致,以该 SHA 复核历史,并通过 `--match-head-commit`
Expand All @@ -17,8 +24,8 @@
- Model 继承 `ModelIdentity<Model, 能力接口>`、Backend Provider 继承 `BackendIdentity<Backend>`,
身份只声明一次,Definition 从 `MakeModelDefinition` / `MakeBackendDefinition` 开始;
配置读取使用 `ConfigValueOrDefault`,默认值只写在 `config_fields`。
- 转换器 `external_type` 留空时由外部槽类型推导;`InputPortBindings` / `OutputPortBindings`
共用 `PortBindings<方向>`;`ResolvedInputLimits` 更名为 `InputLimits` 且不再出现在部署配置中。
- `InputPortBindings` / `OutputPortBindings` 共用 `PortBindings<方向>`;`ResolvedInputLimits`
更名为 `InputLimits` 且不再出现在部署配置中。
- 删除 `include/adapter/biz_results.h`、`ModelManager::RegisterModel`(改用 `RegisterBatch`)和
`RuntimeOptions` 中只写不读的 `biz_type`、`depth_num`、`biz_name`;`NodeBase` 的类写法端口辅助
函数移到 `dev_support` 的 `LegacyNodeBase`。
Expand Down
6 changes: 3 additions & 3 deletions doc/dev_guide/business_onboarding.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ Operator 的宿主输入校验会拒绝 `CompanyString` 中的原始嵌入 NUL

外部必需槽的常见写法是 `ExternalInputSlot<T>(slot)` 和
`ExternalOutputSlot<T>(slot)`,类型由 traits 推导。
输出容量字段由已注册 ValueType 的字符串容量字段决定;显式列出时必须与该 ValueType 的字段集合一致。
输出容量字段由已注册 ValueType 的字符串容量字段决定,不在槽位上声明。
槽名、类型后缀和外部 key 后缀不全相同、可选槽或特殊布局时,使用完整的 `ExternalSlotDefinition`,
各字段含义和示例见[选择参数](operator_output_allocation.md#选择参数)。

Expand Down Expand Up @@ -206,8 +206,8 @@ ValueType。宿主类型的转换都留在接入适配层,Node、Model 和 Bac
已有契约的新方案直接沿用对应 Demo 和数据集格式,只准备 Pipeline 与指向它的 `.conf`。
新契约先检查已有 Demo 运行代码是否支持所需载体、槽位及数据集格式,可满足时复用这些代码。
Pipeline 只选择 `io_binding`,Demo 调用 `ResolveOperatorConfigBiz` 解析业务身份并选择 runner。
Profile 不填写业务名。SDK 预检与注册审计共用同业务 binding 的外部协议、载体及槽位一致性检查。Demo 按该契约准备载体,
不能仅因宿主类型相同就复用另一业务契约;同一业务也不能登记不兼容的载体。
Profile 不填写业务名。SDK 预检与注册审计共用同业务 binding 的外部协议(Converter 的 `schema_id`)、载体及槽位一致性检查。Demo 按该契约准备载体,
不能仅因宿主类型相同就复用另一业务契约;同一业务也不能登记载体或协议不兼容的 binding。
这里的数据转换仅指载体构造和结果展示,外部协议的解包、
字段选择与响应组装仍在转换器;不得把原始业务请求预先拆成内部节点输入。
新绑定需要接入统一 Demo 时,按 `keyword_match_demo.cpp` 补齐以下部分:
Expand Down
11 changes: 5 additions & 6 deletions doc/dev_guide/operator_output_allocation.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,11 @@ JSON 读取器将选中值通过 `dump()` 转为拥有自身存储的 `std::stri

转换器声明槽位时,`ExternalInputSlot<T>(slot)` / `ExternalOutputSlot<T>(slot)` 令
`type_suffix = slot_name`,`key_suffix` 留空并回退到 `type_suffix`,只适用于必需槽且三个名称相同的
常见约定;输入工厂的第二参数是 `value_type`,不能用来覆盖后缀。其他情况使用完整的
`ExternalSlotDefinition`。例如逻辑槽名为 `result`、已注册类型后缀为 `entity_out`、外部 key 为
`sdk.answer` 时,分别设置 `slot_name = "result"`、`type_suffix = "entity_out"`、`key_suffix = "answer"`。
`schema_version`、输出 `cardinality`、`capacity_policy` 使用 Definition 的默认值时无需赋值;
`external_type` 留空时由外部槽类型按声明顺序拼接(如 `CompanyFrame,CompanyString`)。
规则不同时显式填写。
常见约定。其他情况使用完整的 `ExternalSlotDefinition`。例如逻辑槽名为 `result`、已注册类型后缀为
`entity_out`、外部 key 为 `sdk.answer` 时,分别设置 `slot_name = "result"`、
`type_suffix = "entity_out"`、`key_suffix = "answer"`。
输出槽的容量字段由 `type_suffix` 对应的 ValueType 决定,不在槽位上重复声明;Catalog 中的
`external_type` 由外部槽类型按声明顺序拼接(如 `CompanyFrame,CompanyString`)。

以下配置来自参与编译的
[测试接入](../../tests/integration/operator/test_operator_api.cpp)和
Expand Down
4 changes: 2 additions & 2 deletions doc/dev_guide/source_layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ src/adapter/
各业务接入绑定在 `src/adapter/biz/` 中声明 `IoBindingDefinition`,通过 `REGISTER_IO_BINDING` 注册。
端口 Definition、回调的 `bindings.Key(port)` 和 `BindIoPort` 映射共用 typed 声明;
非同名映射显式传入逻辑端口与实际 key。常见必需槽可用 `ExternalInputSlot<T>` /
`ExternalOutputSlot<T>` 推导类型和默认同名后缀,输出容量字段由已注册 ValueType 推导;
显式列出时字段集合必须一致,特殊布局仍使用完整定义。
`ExternalOutputSlot<T>` 推导类型和默认同名后缀,输出容量字段由已注册 ValueType 决定;
特殊布局仍使用完整定义。
业务专属实现可按修改关联同文件组织,共享 converter 保留独立引用;不要求为每个业务创建聚合宏或新注册表。
宿主值类型与命名输出分配方案通过 `adapter/operator_value_type.h` 登记;实现只管理
单份结构及嵌套存储,队列、租约和初始化审计归通用机制所有。常见类型直接使用
Expand Down
27 changes: 7 additions & 20 deletions include/adapter/converter_authoring.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,16 @@ inline std::pair<std::string, std::string> BindIoPort(
// 常用约定:必填槽位,type_suffix = slot_name,key_suffix 为空时回退到
// type_suffix。其他后缀或可选槽位请使用 ExternalSlotDefinition 的显式字段。
template <typename T>
inline ExternalSlotDefinition ExternalInputSlot(
std::string slot_name,
std::string value_type = ExternalTypeTraits<T>::TypeName()) {
return {slot_name,
ExternalTypeTraits<T>::TypeName(),
PortDirection::kInput,
true,
std::move(value_type),
slot_name};
inline ExternalSlotDefinition ExternalInputSlot(std::string slot_name) {
return {slot_name, ExternalTypeTraits<T>::TypeName(), PortDirection::kInput,
true, slot_name};
}

// 省略容量字段时由已注册的 ValueType 推导;
// 显式列表必须与该 ValueType 一致。
// 容量字段由已注册的 ValueType 决定。
template <typename T>
inline ExternalSlotDefinition ExternalOutputSlot(
std::string slot_name, std::vector<std::string> capacity_fields = {}) {
return {slot_name,
ExternalTypeTraits<T>::TypeName(),
PortDirection::kOutput,
true,
ExternalTypeTraits<T>::TypeName(),
slot_name,
std::move(capacity_fields)};
inline ExternalSlotDefinition ExternalOutputSlot(std::string slot_name) {
return {slot_name, ExternalTypeTraits<T>::TypeName(), PortDirection::kOutput,
true, slot_name};
}

// Operator 在解码前已检查载体和有效批大小上限,并通过 options 传入;
Expand Down
4 changes: 2 additions & 2 deletions include/adapter/io_binding_registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ std::unordered_map<std::string, std::string> EffectivePortMapping(
const std::unordered_map<std::string, std::string>& declared,
const std::vector<NodePortDefinition>& logical_ports);

// 未声明 capacity_fields 的输出槽位,按字典序继承其已注册 ValueType 的
// 字符串容量字段。输入槽位和未知 ValueType 原样返回声明列表。
// 输出槽位按字典序返回其已注册 ValueType 的字符串容量字段;
// 输入槽位和未知 ValueType 返回空列表。
std::vector<std::string> EffectiveCapacityFields(
const ExternalSlotDefinition& slot);

Expand Down
27 changes: 14 additions & 13 deletions include/adapter/io_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,33 +201,38 @@ struct ExternalSlotDefinition {
std::string type_id;
PortDirection direction = PortDirection::kInput;
bool required = true;
std::string value_type;
std::string type_suffix; // Operator ValueType 规范后缀 (如 "plain_text",
// "entity_out")
std::vector<std::string> capacity_fields;
std::string key_suffix; // 外部 map key 后缀 (为空时使用 type_suffix)

ExternalSlotDefinition() = default;
ExternalSlotDefinition(std::string slot_name, std::string type_id,
PortDirection direction = PortDirection::kInput,
bool required = true, std::string value_type = "",
std::string type_suffix = "",
std::vector<std::string> capacity_fields = {},
bool required = true, std::string type_suffix = "",
std::string key_suffix = "")
: slot_name(std::move(slot_name)),
type_id(std::move(type_id)),
direction(direction),
required(required),
value_type(std::move(value_type)),
type_suffix(std::move(type_suffix)),
capacity_fields(std::move(capacity_fields)),
key_suffix(std::move(key_suffix)) {}

const std::string& KeySuffix() const {
return !key_suffix.empty() ? key_suffix : type_suffix;
}
};

// 外部载体类型标签:按声明顺序拼接槽位类型,如 "CompanyFrame,CompanyString"。
inline std::string ExternalType(
const std::vector<ExternalSlotDefinition>& slots) {
std::string joined;
for (const auto& slot : slots) {
if (!joined.empty()) joined += ",";
joined += slot.type_id;
}
return joined;
}

// 统一输入/输出转换回调函数指针类型
using DecodeInputFn = int (*)(const ExternalInputBatchView& source,
const InputDecodeOptions& options,
Expand All @@ -246,9 +251,8 @@ using EncodeOutputFn = int (*)(AlgContext* context,
struct InputConverterDefinition {
std::string converter_id;

// 外部载荷协议 ID。载体相同时,同一 biz 的多个 Binding 依靠它区分解析语义。
std::string schema_id;
int schema_version = 1;
std::string external_type;
std::vector<ExternalSlotDefinition> external_slots;
std::vector<NodePortDefinition> logical_ports; // 发布的内部逻辑输出端口
// 可选的 Converter 专属上限;0 表示不设上限。
Expand All @@ -263,15 +267,12 @@ struct InputConverterDefinition {
struct OutputConverterDefinition {
std::string converter_id;

// 外部响应协议 ID,规则同 InputConverterDefinition::schema_id。
std::string schema_id;
int schema_version = 1;
std::string external_type;
std::vector<NodePortDefinition> logical_ports; // 消费的内部逻辑输入端口
std::vector<ExternalSlotDefinition> external_slots;
std::string cardinality = "1:1";
// 可选的 Converter 专属上限;0 表示不设上限。
size_t max_batch_size = 0;
std::string capacity_policy = "reject_overflow";

EncodeOutputFn encode_fn = nullptr;
};
Expand Down
6 changes: 3 additions & 3 deletions src/adapter/input/text_input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ InputConverterDefinition MakeOperatorEntityInputConverter() {
def.schema_id = "text.plain.request";

def.external_slots = {
ExternalInputSlot<CompanyOperatorEntityInput>(kEntitySlot, kEntitySlot)};
ExternalInputSlot<CompanyOperatorEntityInput>(kEntitySlot)};
def.logical_ports = {OutputPort(kInputSentences)};
def.decode_fn = &DecodeOperatorEntityInput;
return def;
Expand All @@ -69,8 +69,8 @@ InputConverterDefinition MakeOperatorKeywordInputConverter() {

def.schema_id = "text.plain.request";

def.external_slots = {ExternalInputSlot<CompanyOperatorKeywordInput>(
kKeywordSlot, kKeywordSlot)};
def.external_slots = {
ExternalInputSlot<CompanyOperatorKeywordInput>(kKeywordSlot)};
def.logical_ports = {OutputPort(kInputSentences)};
def.decode_fn = &DecodeOperatorKeywordInput;
return def;
Expand Down
2 changes: 1 addition & 1 deletion src/adapter/input/translate_json_input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ InputConverterDefinition MakeOperatorTranslateJsonInputConverter() {
def.schema_id = "translate.json.request";

def.external_slots = {
ExternalInputSlot<CompanyOperatorEntityInput>(kInputSlot, kInputSlot)};
ExternalInputSlot<CompanyOperatorEntityInput>(kInputSlot)};
def.logical_ports = {OutputPort(kInputSentences)};
def.decode_fn = &DecodeOperatorTranslateJson;
return def;
Expand Down
38 changes: 8 additions & 30 deletions src/adapter/io_binding_registry.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include "adapter/io_binding_registry.h"

#include <algorithm>
#include <set>
#include <tuple>

#include "adapter/io_converter_registry.h"
Expand All @@ -11,31 +10,27 @@
namespace llm_edgeflow {
namespace {

// 容量字段由 type_suffix 对应的 ValueType 决定,无需单独比较。
bool SameExternalSlots(const std::vector<ExternalSlotDefinition>& left,
const std::vector<ExternalSlotDefinition>& right) {
const auto signature = [](const auto& slots) {
using Slot =
std::tuple<std::string, std::string, std::string, PortDirection, bool,
std::string, std::vector<std::string>>;
std::tuple<std::string, std::string, std::string, PortDirection, bool>;
std::vector<Slot> result;
for (const auto& slot : slots) {
auto capacities = EffectiveCapacityFields(slot);
std::sort(capacities.begin(), capacities.end());
result.emplace_back(slot.KeySuffix(), slot.type_id, slot.type_suffix,
slot.direction, slot.required, slot.value_type,
std::move(capacities));
slot.direction, slot.required);
}
std::sort(result.begin(), result.end());
return result;
};
return signature(left) == signature(right);
}

// 载体与槽位相同的 Converter 仍可能按不同协议解析,须同时比较 schema_id。
template <typename Converter>
bool SameExternalContract(const Converter& left, const Converter& right) {
return left.schema_id == right.schema_id &&
left.schema_version == right.schema_version &&
left.external_type == right.external_type &&
SameExternalSlots(left.external_slots, right.external_slots);
}

Expand All @@ -60,11 +55,8 @@ bool CheckBizContract(std::vector<IoBindingDefinition> bindings,
"' references an unregistered converter";
return false;
}
if (first_input &&
(!SameExternalContract(*first_input, *input) ||
!SameExternalContract(*first_output, *output) ||
first_output->cardinality != output->cardinality ||
first_output->capacity_policy != output->capacity_policy)) {
if (first_input && (!SameExternalContract(*first_input, *input) ||
!SameExternalContract(*first_output, *output))) {
if (error)
*error = "Bindings '" + first_binding + "' and '" + binding.binding_id +
"' for biz_name '" + biz_name +
Expand All @@ -82,11 +74,10 @@ bool CheckBizContract(std::vector<IoBindingDefinition> bindings,

std::vector<std::string> EffectiveCapacityFields(
const ExternalSlotDefinition& slot) {
if (slot.direction != PortDirection::kOutput || !slot.capacity_fields.empty())
return slot.capacity_fields;
if (slot.direction != PortDirection::kOutput) return {};
const auto* binding = OperatorValueTypeRegistry::Instance().GetOutputBinding(
slot.type_suffix, "");
if (!binding) return slot.capacity_fields;
if (!binding) return {};
std::vector<std::string> fields;
for (const auto& [name, config] :
binding->output_layout.string_capacity_fields)
Expand Down Expand Up @@ -393,19 +384,6 @@ bool IoBindingRegistry::Audit(std::vector<std::string>* out_errors) const {
errors.push_back(
"Binding '" + binding_id + "' output slot '" + slot.slot_name +
"' uses unregistered ValueType suffix: " + slot.type_suffix);
} else if (!slot.capacity_fields.empty()) {
std::set<std::string> expected;
for (const auto& [name, config] :
val_binding->output_layout.string_capacity_fields)
expected.insert(name);
const std::set<std::string> declared(slot.capacity_fields.begin(),
slot.capacity_fields.end());
if (declared != expected) {
errors.push_back("Binding '" + binding_id + "' output slot '" +
slot.slot_name +
"' capacity_fields do not match ValueType '" +
slot.type_suffix + "'");
}
}
}
}
Expand Down
9 changes: 2 additions & 7 deletions src/adapter/io_catalog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ nlohmann::json SlotJson(const ExternalSlotDefinition& slot) {
{"key_suffix", slot.KeySuffix()},
{"direction",
slot.direction == PortDirection::kInput ? "input" : "output"},
{"value_type", slot.value_type},
{"required", slot.required},
{"capacity_fields", EffectiveCapacityFields(slot)}};
}
Expand All @@ -33,8 +32,7 @@ nlohmann::json InputConverterToJson(const InputConverterDefinition& conv) {
return {{"converter_id", conv.converter_id},
{"transport", "operator"},
{"schema_id", conv.schema_id},
{"schema_version", conv.schema_version},
{"external_type", conv.external_type},
{"external_type", ExternalType(conv.external_slots)},
{"max_batch_size", conv.max_batch_size},
{"external_slots", std::move(slots)},
{"logical_ports", std::move(ports)}};
Expand All @@ -50,11 +48,8 @@ nlohmann::json OutputConverterToJson(const OutputConverterDefinition& conv) {
return {{"converter_id", conv.converter_id},
{"transport", "operator"},
{"schema_id", conv.schema_id},
{"schema_version", conv.schema_version},
{"external_type", conv.external_type},
{"external_type", ExternalType(conv.external_slots)},
{"max_batch_size", conv.max_batch_size},
{"cardinality", conv.cardinality},
{"capacity_policy", conv.capacity_policy},
{"external_slots", std::move(slots)},
{"logical_ports", std::move(ports)}};
}
Expand Down
Loading
Loading