This commit is contained in:
lasta
2026-05-22 00:22:03 +09:00
parent 8d77bc659f
commit d841fb823a
49 changed files with 2732 additions and 3763 deletions

View File

@@ -52,6 +52,11 @@ const startResearchSchema = z.object({
max_branch: z.number().int().min(1).max(30),
min_relevance: z.number().min(0).max(1),
same_domain_only: z.boolean(),
extraction_mode: z.enum(["rule_only", "llm_only", "hybrid", "compare"]),
extractor_provider: z.enum(["lm_studio", "openai", "ollama"]),
extractor_model: z.string().optional(),
extractor_base_url: z.string().optional(),
fallback_to_rules: z.boolean(),
});
type StartResearchFormValues = z.infer<typeof startResearchSchema>;
@@ -89,6 +94,7 @@ export default function ResearchPage() {
register,
handleSubmit,
setValue,
watch,
formState: { errors, isSubmitting },
} = useForm<StartResearchFormValues>({
resolver: zodResolver(startResearchSchema),
@@ -101,12 +107,18 @@ export default function ResearchPage() {
max_branch: 8,
min_relevance: 0.35,
same_domain_only: true,
extraction_mode: "hybrid",
extractor_provider: "lm_studio",
extractor_model: "",
extractor_base_url: "http://localhost:1234/v1",
fallback_to_rules: true,
},
});
const onSubmit = async (values: StartResearchFormValues) => {
try {
setLastResult(null);
const usesLlm = values.extraction_mode !== "rule_only";
const res = await startResearch.mutateAsync({
project_name: projectName,
source_name: values.source_name,
@@ -117,6 +129,11 @@ export default function ResearchPage() {
max_branch: values.max_branch,
min_relevance: values.min_relevance,
same_domain_only: values.same_domain_only,
extraction_mode: values.extraction_mode,
extractor_provider: values.extractor_provider,
extractor_model: usesLlm ? values.extractor_model || null : null,
extractor_base_url: usesLlm ? values.extractor_base_url || null : null,
fallback_to_rules: values.fallback_to_rules,
});
setLastResult(res);
toast.success(t("research.completed", "자율 연구가 완료되었습니다"));
@@ -130,6 +147,8 @@ export default function ResearchPage() {
};
const sources = project?.sources ?? [];
const extractionMode = watch("extraction_mode");
const usesLlm = extractionMode !== "rule_only";
return (
<div className="mx-auto max-w-6xl px-6 py-10">
@@ -344,6 +363,60 @@ export default function ResearchPage() {
</span>
</label>
<div className="space-y-3 rounded-md border bg-background-subtle p-3">
<div className="grid grid-cols-2 gap-3">
<div className="space-y-1.5">
<Label htmlFor="extraction_mode">Extraction mode</Label>
<Select id="extraction_mode" {...register("extraction_mode")}>
<option value="hybrid">Hybrid</option>
<option value="rule_only">Rule only</option>
<option value="llm_only">LLM only</option>
<option value="compare">Compare</option>
</Select>
</div>
<div className="space-y-1.5">
<Label htmlFor="extractor_provider">Provider</Label>
<Select
id="extractor_provider"
disabled={!usesLlm}
{...register("extractor_provider")}
>
<option value="lm_studio">LM Studio</option>
<option value="openai">OpenAI</option>
<option value="ollama">Ollama</option>
</Select>
</div>
</div>
{usesLlm && (
<>
<div className="space-y-1.5">
<Label htmlFor="extractor_model">Model</Label>
<Input
id="extractor_model"
placeholder="deepseek-r1-distill-qwen-7b"
{...register("extractor_model")}
/>
</div>
<div className="space-y-1.5">
<Label htmlFor="extractor_base_url">Base URL</Label>
<Input
id="extractor_base_url"
placeholder="http://localhost:1234/v1"
{...register("extractor_base_url")}
/>
</div>
<label className="flex items-center gap-2 text-sm">
<input
type="checkbox"
className="h-4 w-4 rounded border-input"
{...register("fallback_to_rules")}
/>
<span>Fallback to rules</span>
</label>
</>
)}
</div>
<Button
type="submit"
className="w-full"