refactor: unify data preparations and research input snapshots
Deploy production / deploy (push) Successful in 53s
Deploy production / deploy (push) Successful in 53s
This commit is contained in:
@@ -222,7 +222,7 @@ export function ExperimentView({
|
||||
})
|
||||
}
|
||||
>
|
||||
{input.dataset_id} · {input.scope.region}/{input.scope.universe}/D
|
||||
{input.name} · {input.scope.region}/{input.scope.universe}/D
|
||||
{input.scope.delay}
|
||||
</Button>
|
||||
))}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import {
|
||||
ResearchDataInput,
|
||||
researchSelection,
|
||||
} from "../preparations/ResearchDataInput";
|
||||
import { ResearchList, ResearchDetail } from "./ResearchList";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
@@ -66,9 +70,7 @@ export function FeaturesPage({
|
||||
`/research/assets?kind=feature&q=${encodeURIComponent(q)}&offset=${(page - 1) * 25}`,
|
||||
{ signal: controller.signal },
|
||||
),
|
||||
api<{ items: InputSnapshot[] }>("/research/inputs", {
|
||||
signal: controller.signal,
|
||||
}),
|
||||
Promise.resolve({ items: inputs }),
|
||||
])
|
||||
.then(([list, fixed]) => {
|
||||
setItems(list.items);
|
||||
@@ -128,7 +130,7 @@ export function FeaturesPage({
|
||||
body: JSON.stringify({
|
||||
kind: "feature",
|
||||
version: asset?.version,
|
||||
content: draft,
|
||||
content: { ...draft, ...researchSelection(draft.input_ids, inputs) },
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -317,7 +319,7 @@ export function FeaturesPage({
|
||||
.filter((i) => draft.input_ids.includes(i.id))
|
||||
.map((i) => ({
|
||||
id: i.id,
|
||||
name: `${i.dataset_id} · ${i.scope.region}/${i.scope.universe}/D${i.scope.delay} · ${i.field_ids.length} 字段 · ${i.id}`,
|
||||
name: `${i.name} · ${i.scope.region}/${i.scope.universe}/D${i.scope.delay} · ${i.field_ids.length} 字段 · ${i.id}`,
|
||||
}))}
|
||||
disabled={!!busy}
|
||||
busy={busy === "generate"}
|
||||
@@ -330,7 +332,7 @@ export function FeaturesPage({
|
||||
await post<FeatureAsset>("/research/generate", {
|
||||
name: draft.name,
|
||||
hypothesis: draft.hypothesis,
|
||||
input_ids: draft.input_ids,
|
||||
...researchSelection(draft.input_ids, inputs),
|
||||
method: "feature",
|
||||
}),
|
||||
);
|
||||
@@ -347,23 +349,21 @@ export function FeaturesPage({
|
||||
</section>
|
||||
<label>
|
||||
固定输入
|
||||
<ResearchSelect
|
||||
label="特征固定输入"
|
||||
multiple
|
||||
filter
|
||||
value={draft.input_ids}
|
||||
optionList={inputs.map((i) => ({
|
||||
value: i.id,
|
||||
label: `${i.dataset_id} · ${i.scope.region}/${i.scope.universe}/D${i.scope.delay} · ${i.field_ids.length} 字段 · ${i.id.slice(0, 8)}`,
|
||||
}))}
|
||||
onChange={(v) => setDraft({ ...draft, input_ids: v as string[] })}
|
||||
<ResearchDataInput
|
||||
label="选择特征数据准备"
|
||||
ids={draft.input_ids}
|
||||
inputs={inputs}
|
||||
onChange={(ids, rows) => {
|
||||
setInputs(rows);
|
||||
setDraft({ ...draft, input_ids: ids });
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
{inputs
|
||||
.filter((i) => draft.input_ids.includes(i.id))
|
||||
.map((i) => (
|
||||
<details key={i.id}>
|
||||
<summary>{i.dataset_id} 的固定字段</summary>
|
||||
<summary>{i.name} 的固定字段</summary>
|
||||
<p className="muted">{i.field_ids.join("、")}</p>
|
||||
</details>
|
||||
))}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import {
|
||||
ResearchDataInput,
|
||||
researchSelection,
|
||||
} from "../preparations/ResearchDataInput";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
Banner,
|
||||
@@ -54,7 +58,7 @@ export function FlowLaunchForm({
|
||||
useEffect(() => {
|
||||
const c = new AbortController();
|
||||
Promise.all([
|
||||
api<{ items: InputSnapshot[] }>("/research/inputs", { signal: c.signal }),
|
||||
Promise.resolve({ items: inputs }),
|
||||
api<{ items: Asset[] }>("/research/assets?kind=template&limit=100", {
|
||||
signal: c.signal,
|
||||
}),
|
||||
@@ -73,7 +77,7 @@ export function FlowLaunchForm({
|
||||
setConfirmation({
|
||||
request_id: crypto.randomUUID(),
|
||||
name,
|
||||
input_ids: ids,
|
||||
...researchSelection(ids, inputs),
|
||||
hypothesis,
|
||||
settings,
|
||||
budget,
|
||||
@@ -133,22 +137,17 @@ export function FlowLaunchForm({
|
||||
</label>
|
||||
<label>
|
||||
固定数据范围
|
||||
<ResearchSelect
|
||||
label="自动研究固定输入"
|
||||
multiple
|
||||
filter
|
||||
value={ids}
|
||||
optionList={inputs.map((i) => ({
|
||||
value: i.id,
|
||||
label: `${i.dataset_id} · ${i.scope.region}/${i.scope.universe}/D${i.scope.delay} · ${i.id.slice(0, 8)}`,
|
||||
}))}
|
||||
onChange={(v) => {
|
||||
const next = v as string[];
|
||||
setIds(next);
|
||||
const first = inputs.find((i) => i.id === next[0]);
|
||||
<ResearchDataInput
|
||||
label="选择研究数据准备"
|
||||
ids={ids}
|
||||
inputs={inputs}
|
||||
onChange={(ids, rows) => {
|
||||
setInputs(rows);
|
||||
setIds(ids);
|
||||
const first = rows.find((r) => r.id === ids[0]);
|
||||
if (first)
|
||||
setSettings((s) => ({
|
||||
...s,
|
||||
setSettings((old) => ({
|
||||
...old,
|
||||
region: first.scope.region,
|
||||
universe: first.scope.universe,
|
||||
delay: first.scope.delay,
|
||||
@@ -290,8 +289,10 @@ export function FlowLaunchForm({
|
||||
</p>
|
||||
)}
|
||||
<p>
|
||||
{confirmation.input_ids.length} 个固定输入 · {settings.region}/
|
||||
{settings.universe}/D{settings.delay}
|
||||
{confirmation.input_ids.length +
|
||||
(confirmation.preparation_refs?.length ?? 0)}{" "}
|
||||
个固定输入 · {settings.region}/{settings.universe}/D
|
||||
{settings.delay}
|
||||
</p>
|
||||
<ul>
|
||||
{confirmation.input_ids.map((id) => {
|
||||
@@ -299,7 +300,7 @@ export function FlowLaunchForm({
|
||||
return (
|
||||
<li key={id}>
|
||||
{input
|
||||
? `${input.dataset_id} · ${input.scope.region}/${input.scope.universe}/D${input.scope.delay}`
|
||||
? `${input.name} · ${input.scope.region}/${input.scope.universe}/D${input.scope.delay}`
|
||||
: id}{" "}
|
||||
· {id.slice(0, 8)}
|
||||
</li>
|
||||
@@ -315,7 +316,9 @@ export function FlowLaunchForm({
|
||||
{confirmation.template_id && (
|
||||
<p>
|
||||
初始模板:
|
||||
{templates.find((t) => t.id === confirmation.template_id)?.name}{" "}
|
||||
{
|
||||
templates.find((t) => t.id === confirmation.template_id)?.name
|
||||
}{" "}
|
||||
· v{confirmation.template_version}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import {
|
||||
ResearchDataInput,
|
||||
researchSelection,
|
||||
} from "../preparations/ResearchDataInput";
|
||||
import { ResearchList, ResearchDetail } from "./ResearchList";
|
||||
import { ResearchSelect } from "./ResearchSelect";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
@@ -101,7 +105,7 @@ export function ResearchWorkspace({
|
||||
api<{ items: Asset[]; total: number }>(
|
||||
`/research/assets?kind=template&limit=25&offset=${assetPage * 25}&q=${encodeURIComponent(search)}`,
|
||||
),
|
||||
api<{ items: InputSnapshot[] }>("/research/inputs"),
|
||||
Promise.resolve({ items: inputs }),
|
||||
api<{ items: typeof history; total: number }>(
|
||||
`/research/experiments?limit=25&offset=${historyPage * 25}`,
|
||||
),
|
||||
@@ -129,7 +133,11 @@ export function ResearchWorkspace({
|
||||
research_asset_id: asset?.id,
|
||||
research_experiment_id: experiment?.id,
|
||||
alpha_id: parent.split(/[,,\s]+/)[0] || undefined,
|
||||
template_input_id: inputIds.length === 1 ? inputIds[0] : undefined,
|
||||
input_snapshot_id:
|
||||
inputIds.length === 1 &&
|
||||
!inputs.find((i) => i.id === inputIds[0])?.preparation_ref
|
||||
? inputIds[0]
|
||||
: undefined,
|
||||
});
|
||||
}, [active, page, asset?.id, experiment?.id, parent, inputIds, onContext]);
|
||||
useEffect(() => {
|
||||
@@ -180,17 +188,6 @@ export function ResearchWorkspace({
|
||||
setMethod("structure");
|
||||
}
|
||||
}, [active, action]);
|
||||
function selectInputs(ids: string[]) {
|
||||
setInputIds(ids);
|
||||
const first = inputs.find((item) => item.id === ids[0]);
|
||||
if (first)
|
||||
setSettings((old) => ({
|
||||
...old,
|
||||
region: first.scope.region,
|
||||
universe: first.scope.universe,
|
||||
delay: first.scope.delay,
|
||||
}));
|
||||
}
|
||||
async function refreshSettings() {
|
||||
const data = await post<{
|
||||
content: {
|
||||
@@ -236,7 +233,7 @@ export function ResearchWorkspace({
|
||||
const next = await post<Asset>("/research/generate", {
|
||||
name: template.name,
|
||||
hypothesis,
|
||||
input_ids: inputIds,
|
||||
...researchSelection(inputIds, inputs),
|
||||
parent_alpha_ids: parent ? parent.split(/[,,\s]+/).filter(Boolean) : [],
|
||||
method: page === "variants" ? "structure" : "template",
|
||||
});
|
||||
@@ -274,7 +271,7 @@ export function ResearchWorkspace({
|
||||
const next = await post<Experiment>("/research/experiments", {
|
||||
asset_id: asset!.id,
|
||||
version: asset!.version,
|
||||
input_ids: inputIds,
|
||||
...researchSelection(inputIds, inputs),
|
||||
hypothesis,
|
||||
settings,
|
||||
mode,
|
||||
@@ -580,20 +577,26 @@ export function ResearchWorkspace({
|
||||
)}
|
||||
<label>
|
||||
固定研究输入
|
||||
<ResearchSelect
|
||||
multiple
|
||||
filter
|
||||
label="固定研究输入"
|
||||
value={inputIds}
|
||||
optionList={inputs.map((input) => ({
|
||||
value: input.id,
|
||||
label: `${input.dataset_id} · ${input.scope.region}/${input.scope.universe}/D${input.scope.delay} · ${input.field_ids.length} 字段 · ${input.id.slice(0, 8)}`,
|
||||
}))}
|
||||
onChange={(value) => selectInputs(value as string[])}
|
||||
<ResearchDataInput
|
||||
label="选择数据准备"
|
||||
ids={inputIds}
|
||||
inputs={inputs}
|
||||
onChange={(ids, rows) => {
|
||||
setInputs(rows);
|
||||
setInputIds(ids);
|
||||
const first = rows.find((r) => r.id === ids[0]);
|
||||
if (first)
|
||||
setSettings((old) => ({
|
||||
...old,
|
||||
region: first.scope.region,
|
||||
universe: first.scope.universe,
|
||||
delay: first.scope.delay,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
<p className="research-hint">
|
||||
在数据目录中保存字段选择。跨数据集分别关联输入,跨市场使用目标范围的独立输入。
|
||||
从数据准备选择集合;每个集合可包含同范围下多个数据集的字段。
|
||||
</p>
|
||||
{page === "variants" && method === "settings" ? (
|
||||
<label>
|
||||
@@ -624,7 +627,7 @@ export function ResearchWorkspace({
|
||||
references={[
|
||||
...selectedInputs.map((input) => ({
|
||||
id: input.id,
|
||||
name: `${input.dataset_id} · ${input.scope.region}/${input.scope.universe}/D${input.scope.delay} · ${input.field_ids.length} 字段 · ${input.id}`,
|
||||
name: `${input.name} · ${input.scope.region}/${input.scope.universe}/D${input.scope.delay} · ${input.field_ids.length} 字段 · ${input.id}`,
|
||||
})),
|
||||
...(page === "variants" && parent
|
||||
? [
|
||||
@@ -857,7 +860,7 @@ export function ResearchWorkspace({
|
||||
setExperiment(
|
||||
await post("/research/variants/settings", {
|
||||
alpha_id: parent,
|
||||
input_ids: inputIds,
|
||||
...researchSelection(inputIds, inputs),
|
||||
...(hypothesis ? { hypothesis } : {}),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -62,19 +62,28 @@ export function SourceDetails({
|
||||
打开研究会话
|
||||
</Button>
|
||||
)}
|
||||
{source.template_input_id && (
|
||||
{[
|
||||
...new Set([
|
||||
...(source.input_snapshot_ids ?? []),
|
||||
...(source.input_snapshot_id ? [source.input_snapshot_id] : []),
|
||||
]),
|
||||
].map((id, index) => (
|
||||
<Button
|
||||
key={id}
|
||||
onClick={() =>
|
||||
onAction({
|
||||
type: "open_research_input",
|
||||
input_id: source.template_input_id!,
|
||||
input_id: id,
|
||||
nonce: Date.now(),
|
||||
})
|
||||
}
|
||||
>
|
||||
查看研究输入
|
||||
{(source.input_snapshot_ids?.length ?? 0) > 1
|
||||
? ` ${index + 1}`
|
||||
: ""}
|
||||
</Button>
|
||||
)}
|
||||
))}
|
||||
{source.parent_run_id && (
|
||||
<Button
|
||||
onClick={() =>
|
||||
|
||||
@@ -21,6 +21,7 @@ export type FlowLaunch = {
|
||||
request_id: string;
|
||||
name: string;
|
||||
input_ids: string[];
|
||||
preparation_refs?: { id: string; version: number }[];
|
||||
hypothesis: string;
|
||||
settings: SimulationSettings;
|
||||
budget: Budget;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { FieldRecord, PreparationRef } from "../preparations/types";
|
||||
import type { Candidate } from "../backtests/types";
|
||||
export type Variable = {
|
||||
kind:
|
||||
@@ -43,10 +44,14 @@ export type InputSnapshot = {
|
||||
universe: string;
|
||||
delay: 0 | 1;
|
||||
};
|
||||
dataset_id: string;
|
||||
name: string;
|
||||
dataset_ids: string[];
|
||||
fields: FieldRecord[];
|
||||
preparation_ref?: PreparationRef;
|
||||
field_ids: string[];
|
||||
field_types: Record<string, string>;
|
||||
collection_version: string;
|
||||
preparation_id?: string;
|
||||
preparation_version?: number;
|
||||
created_at: string;
|
||||
};
|
||||
export type ResearchCandidate = Candidate & {
|
||||
|
||||
Reference in New Issue
Block a user