This commit is contained in:
@@ -116,7 +116,7 @@ class Control(Contract):
|
||||
class CreateTemplate(Contract):
|
||||
template: TemplateSpec
|
||||
hypothesis: str = Field(min_length=1, max_length=10000)
|
||||
source_item_ids: list[RunId] = Field(min_length=1, max_length=20)
|
||||
source_item_ids: list[RunId] = Field(default_factory=list, max_length=20)
|
||||
reference: str | None = Field(default=None, max_length=200)
|
||||
idempotency_key: Identifier
|
||||
|
||||
@@ -147,6 +147,27 @@ class TemplateSearch(Page):
|
||||
q: str = Field(default="", max_length=200)
|
||||
|
||||
|
||||
class TemplateExpansion(Contract):
|
||||
template_id: RunId
|
||||
version: int = Field(ge=1)
|
||||
preparation_refs: list[PreparationReference] = Field(min_length=1, max_length=20)
|
||||
settings: CompleteSettings
|
||||
mode: Literal["all", "random"] = "all"
|
||||
limit: int = Field(default=100, ge=1, le=10000)
|
||||
seed: int = 0
|
||||
idempotency_key: Identifier
|
||||
|
||||
|
||||
class TemplateCandidates(Page):
|
||||
experiment_id: RunId
|
||||
|
||||
|
||||
class SubmitTemplateBacktest(Contract):
|
||||
experiment_id: RunId
|
||||
candidate_ids: list[Identifier] = Field(min_length=1, max_length=10000)
|
||||
idempotency_key: Identifier
|
||||
|
||||
|
||||
class CatalogSearch(Contract):
|
||||
filters: CatalogFilters
|
||||
dataset_id: str | None = Field(default=None, min_length=1, max_length=200)
|
||||
|
||||
@@ -83,7 +83,11 @@ class ResearchAccess(SuperResearchAccess):
|
||||
"create_with": "create_research_template", "required_scope": "research:write",
|
||||
"version_with": "create_research_template_version",
|
||||
"read_with": "get_research_template", "search_with": "search_research_templates",
|
||||
"authored_by": "caller", "max_source_items": 20,
|
||||
"authored_by": "caller", "source_items_required": False, "max_source_items": 20,
|
||||
"expand_with": "expand_research_template", "candidates_with": "get_template_candidates",
|
||||
"backtest_with": "start_template_backtest", "execution_scope": "backtests:execute",
|
||||
"max_candidates": 10000, "page_size_max": 100,
|
||||
"validation": "syntax_and_preparation_settings_combination",
|
||||
"source_items_with": "get_backtest_results", "starts_backtests": False,
|
||||
"web_url": f"{self.public_origin}/#templates",
|
||||
},
|
||||
@@ -311,6 +315,52 @@ class ResearchAccess(SuperResearchAccess):
|
||||
result["web_url"] = f"{self.public_origin}/#templates"
|
||||
return await self.remember(operation, args, digest, result, business_id=args.template_id)
|
||||
|
||||
async def expand_template(self, args):
|
||||
"""Idempotently freeze a version and preparations; never start execution."""
|
||||
from ..research.experiments import Experiments
|
||||
from ..research.workspace_contracts import Expansion
|
||||
|
||||
operation = "expand_research_template"
|
||||
previous, digest = await self.previous(operation, args)
|
||||
if previous:
|
||||
return previous.response
|
||||
service = Experiments(self.db)
|
||||
asset = await service.assets.get(args.template_id, args.version, "template")
|
||||
experiment = await service.create(Expansion(
|
||||
asset_id=args.template_id, version=args.version, preparation_refs=args.preparation_refs,
|
||||
settings=args.settings, mode=args.mode, limit=args.limit, seed=args.seed,
|
||||
hypothesis=asset["content"].get("description", "").strip() or f"使用模板:{asset['name']}",
|
||||
), extra_evidence={"method": "template", "mcp_token_id": self.principal.token_id,
|
||||
"admin_id": self.principal.admin_id})
|
||||
result = await service.template_candidates(experiment["id"])
|
||||
result.update({"read_with": "get_template_candidates", "backtest_with": "start_template_backtest",
|
||||
"starts_backtests": False})
|
||||
return await self.remember(operation, args, digest, result, business_id=experiment["id"])
|
||||
|
||||
async def template_candidates(self, args):
|
||||
from ..research.experiments import Experiments
|
||||
|
||||
return await Experiments(self.db).template_candidates(args.experiment_id, args.limit, args.offset)
|
||||
|
||||
async def start_template_backtest(self, args):
|
||||
"""Execute only stored candidate IDs; authorization comes from the caller's execute scope."""
|
||||
from ..research.experiments import Experiments
|
||||
from ..research.workspace_contracts import TemplateBacktest
|
||||
|
||||
operation = "start_template_backtest"
|
||||
previous, digest = await self.previous(operation, args)
|
||||
if previous:
|
||||
return previous.response
|
||||
provenance = {"mcp_token_id": self.principal.token_id, "admin_id": self.principal.admin_id}
|
||||
result = await Experiments(self.db).start_template_backtest(
|
||||
args.experiment_id,
|
||||
TemplateBacktest(candidate_ids=args.candidate_ids, idempotency_key="mcp-template-" + str(uuid4())),
|
||||
backtests=Backtests(self.db, provenance),
|
||||
)
|
||||
result = {**result, "input_digest": digest, "web_url": self.run_url(result["backtest_run_id"])}
|
||||
return await self.remember(operation, args, digest, result,
|
||||
business_id=result["backtest_run_id"], wake="backtests")
|
||||
|
||||
async def previous(self, operation, args):
|
||||
# PostgreSQL row lock is shared with HTTP start and catalog/job creation.
|
||||
account = await self.db.scalar(select(Account).where(Account.id == self.principal.account_id).with_for_update())
|
||||
|
||||
@@ -62,7 +62,7 @@ async def create_template(db, args, principal, *, asset_id=None):
|
||||
**asset, "template_id": asset["id"],
|
||||
"combination_count": (str(math.prod(len(v.values) for v in args.template.variables.values()))
|
||||
if all(v.values for v in args.template.variables.values()) else None),
|
||||
"validation": {"structure": "valid", "source_evidence": "recorded",
|
||||
"validation": {"structure": "valid", "source_evidence": "recorded" if args.source_item_ids else "not_provided",
|
||||
"expanded_candidates": "not_validated", "platform_semantics": "unknown"},
|
||||
"next_step": "在模板工坊选择固定输入及模拟设置,展开并核验候选,再确认批量回测。",
|
||||
"next_step": "选择数据准备和回测参数,用 expand_research_template 生成候选集合;核对候选后按已获授权范围调用 start_template_backtest。也可在模板工坊完成。",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user