feat: integrate chatbox research with datasets and backtests

This commit is contained in:
yuxuanhui
2026-09-08 12:43:00 +08:00
parent 43336ad960
commit aef8e1d310
37 changed files with 1421 additions and 42 deletions
+87 -1
View File
@@ -6,6 +6,13 @@ from typing import Literal
from pydantic import Field
from ..backtests.contracts import ControlInput, PreviewInput, RerunInput, StartInput
from ..catalog.contracts import UNIVERSES, CatalogFilters, Scope
from ..research.contracts import (
ChatboxResearchInput,
InputPageArgs,
ResearchInputSelection,
ResearchPreviewInput,
)
from ..schemas import AlphaFilters, BulkInput, BulkUpdate, Contract, JobInput, ResearchInput, ResearchUpdate
@@ -52,6 +59,30 @@ class BacktestListArgs(Contract):
limit: int = Field(default=20, ge=1, le=100)
offset: int = Field(default=0, ge=0)
source: str | None = Field(default=None, max_length=100)
reference: str | None = Field(default=None, max_length=200)
research_id: str | None = Field(default=None, max_length=200)
class CatalogSearchArgs(Contract):
filters: CatalogFilters
dataset_id: str | None = Field(default=None, min_length=1, max_length=200)
class CatalogDetailArgs(Contract):
scope: Scope
dataset_id: str = Field(min_length=1, max_length=200)
field_id: str = Field(default="", max_length=200)
class BacktestDraftArgs(Contract):
draft_id: str = Field(min_length=1, max_length=36)
limit: int = Field(default=25, ge=1, le=100)
offset: int = Field(default=0, ge=0)
class AlphaSourcesArgs(AlphaArgs):
limit: int = Field(default=25, ge=1, le=100)
offset: int = Field(default=0, ge=0)
class BacktestResultsArgs(BacktestRunArgs):
@@ -74,6 +105,32 @@ class BacktestRerunArgs(BacktestRunArgs):
CATALOG = {
"get_catalog_scopes": (EmptyArgs, "读取本版支持的研究范围组合,不表示账户已获平台权限。"),
"search_catalog": (
CatalogSearchArgs,
"分页查询本地目录。省略 dataset_id 查询数据集;提供 dataset_id 查询其字段、类型和完整集合版本。无缓存时说明需在数据集页同步,不编造字段。",
),
"get_catalog_detail": (CatalogDetailArgs, "读取指定范围的数据集或字段详情;field_id 为空表示数据集。"),
"prepare_research_input": (
ResearchInputSelection,
"把明确选出的 1–100 个字段固定为研究输入快照;必须提供已读取的集合版本。只保存本地输入,不启动同步或回测。已有输入引用时直接读取,不重建。",
),
"get_research_input": (
InputPageArgs,
"分页读取不可变研究输入及原版本字段说明。q 仅搜索字段 ID,类型筛选不改变输入。field_count 是输入总数,total 是筛选匹配数。",
),
"prepare_research_backtest": (
ChatboxResearchInput,
"从研究输入快照构建固定回测预览:提供假设、候选模板(如 rank({price}))、具名字段绑定及真实类型、明确模拟参数。服务端校验归属/类型/范围并替换占位符;不验证算子语义、不自动聚合 VECTOR。来源由服务端标记为当前 chatbox 研究。",
),
"get_backtest_draft": (
BacktestDraftArgs,
"分页读取已有候选草稿、版本和来源,随后按 draft_id/draft_version 准备预览。",
),
"get_alpha_sources": (
AlphaSourcesArgs,
"分页读取 Alpha 的全部已保存研究来源、关联回测、聊天会话和输入快照。没有记录不推断来源。",
),
"get_backtest_capabilities": (
EmptyArgs,
"读取回测输入 schema、支持类型和本地调度配置,不代表平台剩余额度。",
@@ -146,7 +203,36 @@ def bounded(value):
async def read_tool(business, name, args):
from datetime import timezone
if name == "get_backtest_capabilities":
if name == "get_catalog_scopes":
data = {"universes": UNIVERSES, "instrument_type": "EQUITY", "delays": [0, 1]}
elif name == "search_catalog":
data = await business.catalog.search(args.filters, args.dataset_id)
data.update(
scope=args.filters.model_dump(include=set(Scope.model_fields)), dataset_id=args.dataset_id
)
elif name == "get_catalog_detail":
data = await business.catalog.detail(args.scope, args.dataset_id, args.field_id)
# Saved notes are not required for selection; unsaved drafts never cross this interface.
data.pop("research", None)
elif name == "prepare_research_input":
data = await business.research_builder.select_input(args)
elif name == "get_research_input":
data = await business.research_builder.input_page(**args.model_dump())
elif name == "prepare_research_backtest":
data = await business.research_builder.prepare(ResearchPreviewInput(**args.model_dump()))
elif name == "get_backtest_draft":
data = await business.backtests.draft(args.draft_id)
candidates = data.pop("candidates")
data.update(
items=candidates[args.offset : args.offset + args.limit],
total=len(candidates),
limit=args.limit,
offset=args.offset,
has_more=args.offset + args.limit < len(candidates),
)
elif name == "get_alpha_sources":
data = await business.get_alpha_sources(**args.model_dump())
elif name == "get_backtest_capabilities":
data = await business.backtests.capabilities()
elif name == "prepare_backtest":
data = await business.backtests.preview(args)