|
4 | 4 | import type { PromptResponse } from "$lib/types" |
5 | 5 | import Warning from "$lib/ui/warning.svelte" |
6 | 6 | import type { OptionGroup, Option } from "$lib/ui/fancy_select_types" |
7 | | - import { getStaticPromptDisplayName } from "$lib/utils/run_config_formatters" |
| 7 | + import { client } from "$lib/api_client" |
| 8 | + import { goto } from "$app/navigation" |
| 9 | + import { page } from "$app/stores" |
| 10 | + import { prompt_generator_categories } from "$lib/prompt_generators" |
8 | 11 |
|
9 | 12 | export let prompt_method: string |
10 | 13 | export let linked_model_selection: string | null | undefined = undefined |
|
14 | 17 | export let fine_tune_prompt_id: string | undefined = undefined |
15 | 18 | export let description: string | undefined = undefined |
16 | 19 | export let info_description: string | undefined = undefined |
| 20 | + export let project_id: string | null = null |
| 21 | + export let task_id: string | null = null |
| 22 | +
|
| 23 | + let has_rated_data = false |
| 24 | + let has_repair_data = false |
| 25 | + let data_requirements_checked = false |
| 26 | +
|
| 27 | + $: generator_requirements = build_generator_requirements() |
| 28 | +
|
| 29 | + function build_generator_requirements(): Record< |
| 30 | + string, |
| 31 | + { requires_data: boolean; requires_repairs: boolean } |
| 32 | + > { |
| 33 | + const map: Record< |
| 34 | + string, |
| 35 | + { requires_data: boolean; requires_repairs: boolean } |
| 36 | + > = {} |
| 37 | + for (const category of prompt_generator_categories) { |
| 38 | + for (const template of category.templates) { |
| 39 | + if (template.generator_id) { |
| 40 | + map[template.generator_id] = { |
| 41 | + requires_data: template.requires_data, |
| 42 | + requires_repairs: template.requires_repairs, |
| 43 | + } |
| 44 | + } |
| 45 | + } |
| 46 | + } |
| 47 | + return map |
| 48 | + } |
| 49 | +
|
| 50 | + function generator_disabled_reason(generator_id: string): string | null { |
| 51 | + const req = generator_requirements[generator_id] |
| 52 | + if (!req || !data_requirements_checked) { |
| 53 | + return null |
| 54 | + } |
| 55 | + if (req.requires_repairs && !has_repair_data) { |
| 56 | + return "Requires at least one repaired response in your dataset." |
| 57 | + } |
| 58 | + if (req.requires_data && !has_rated_data) { |
| 59 | + return "Requires at least one rated response in your dataset." |
| 60 | + } |
| 61 | + return null |
| 62 | + } |
| 63 | +
|
| 64 | + // Re-fetch rated/repair data whenever project_id or task_id changes so the |
| 65 | + // generator disabled states stay in sync if the parent swaps tasks. |
| 66 | + let requirements_loaded_key: string | null = null |
| 67 | + $: load_data_requirements(project_id, task_id) |
| 68 | +
|
| 69 | + async function load_data_requirements( |
| 70 | + project_id: string | null, |
| 71 | + task_id: string | null, |
| 72 | + ) { |
| 73 | + if (!project_id || !task_id) { |
| 74 | + requirements_loaded_key = null |
| 75 | + data_requirements_checked = false |
| 76 | + has_rated_data = false |
| 77 | + has_repair_data = false |
| 78 | + return |
| 79 | + } |
| 80 | + const key = `${project_id}:${task_id}` |
| 81 | + if (requirements_loaded_key === key) return |
| 82 | + requirements_loaded_key = key |
| 83 | + data_requirements_checked = false |
| 84 | + has_rated_data = false |
| 85 | + has_repair_data = false |
| 86 | + try { |
| 87 | + const { data, error } = await client.GET( |
| 88 | + "/api/projects/{project_id}/tasks/{task_id}/runs_summaries", |
| 89 | + { |
| 90 | + params: { |
| 91 | + path: { project_id, task_id }, |
| 92 | + }, |
| 93 | + }, |
| 94 | + ) |
| 95 | + // Drop stale responses if the task was swapped while the request was |
| 96 | + // in flight — avoids overwriting the new task's flags with old data. |
| 97 | + if (requirements_loaded_key !== key) return |
| 98 | + if (error) return |
| 99 | + if (data) { |
| 100 | + has_rated_data = data.some( |
| 101 | + (run) => |
| 102 | + run.rating && |
| 103 | + run.rating.value !== null && |
| 104 | + run.rating.value !== undefined, |
| 105 | + ) |
| 106 | + has_repair_data = data.some( |
| 107 | + (run) => run.repair_state?.toLowerCase() === "repaired", |
| 108 | + ) |
| 109 | + } |
| 110 | + } finally { |
| 111 | + if (requirements_loaded_key === key) { |
| 112 | + data_requirements_checked = true |
| 113 | + } |
| 114 | + } |
| 115 | + } |
17 | 116 |
|
18 | 117 | $: options = build_prompt_options( |
19 | 118 | $current_task_prompts, |
20 | 119 | exclude_cot, |
21 | 120 | custom_prompt_name, |
22 | 121 | fine_tune_prompt_id, |
| 122 | + project_id, |
| 123 | + task_id, |
| 124 | + data_requirements_checked, |
| 125 | + has_rated_data, |
| 126 | + has_repair_data, |
23 | 127 | ) |
24 | 128 |
|
25 | 129 | function build_prompt_options( |
26 | 130 | current_task_prompts: PromptResponse | null, |
27 | 131 | exclude_cot: boolean, |
28 | 132 | custom_prompt_name: string | undefined, |
29 | 133 | fine_tune_prompt_id: string | undefined, |
| 134 | + project_id: string | null, |
| 135 | + task_id: string | null, |
| 136 | + // eslint-disable-next-line @typescript-eslint/no-unused-vars |
| 137 | + _requirements_checked: boolean, |
| 138 | + // eslint-disable-next-line @typescript-eslint/no-unused-vars |
| 139 | + _has_rated: boolean, |
| 140 | + // eslint-disable-next-line @typescript-eslint/no-unused-vars |
| 141 | + _has_repair: boolean, |
30 | 142 | ): OptionGroup[] { |
31 | 143 | if (!current_task_prompts) { |
32 | 144 | return [] |
|
39 | 151 | if (generator.chain_of_thought && exclude_cot) { |
40 | 152 | continue |
41 | 153 | } |
| 154 | + const disabled_reason = generator_disabled_reason(generator.id) |
42 | 155 | generators.push({ |
43 | 156 | value: generator.id, |
44 | 157 | label: generator.name, |
45 | | - description: generator.short_description, |
| 158 | + description: disabled_reason ?? generator.short_description, |
| 159 | + disabled: !!disabled_reason, |
46 | 160 | }) |
47 | 161 | } |
48 | 162 | if (generators.length > 0) { |
|
84 | 198 | } |
85 | 199 | static_prompts.push({ |
86 | 200 | value: prompt.id, |
87 | | - label: getStaticPromptDisplayName( |
88 | | - prompt.name, |
89 | | - prompt.generator_id, |
90 | | - current_task_prompts, |
91 | | - ), |
| 201 | + label: prompt.name, |
92 | 202 | }) |
93 | 203 | } |
| 204 | + const saved_prompts_action = |
| 205 | + project_id && task_id |
| 206 | + ? { |
| 207 | + action_label: "Create New", |
| 208 | + action_handler: () => { |
| 209 | + const params = new URLSearchParams() |
| 210 | + const from = $page.url.searchParams.get("from") |
| 211 | + if (from) { |
| 212 | + params.set("from", from) |
| 213 | + } |
| 214 | + const qs = params.toString() |
| 215 | + goto( |
| 216 | + `/prompts/${project_id}/${task_id}/prompt_generators${ |
| 217 | + qs ? `?${qs}` : "" |
| 218 | + }`, |
| 219 | + ) |
| 220 | + }, |
| 221 | + } |
| 222 | + : {} |
94 | 223 | if (static_prompts.length > 0) { |
95 | 224 | grouped_options.push({ |
96 | 225 | label: "Saved Prompts", |
97 | 226 | options: static_prompts, |
| 227 | + ...saved_prompts_action, |
| 228 | + }) |
| 229 | + } else if (project_id && task_id) { |
| 230 | + grouped_options.push({ |
| 231 | + label: "Saved Prompts", |
| 232 | + options: [], |
| 233 | + ...saved_prompts_action, |
98 | 234 | }) |
99 | 235 | } |
100 | 236 | return grouped_options |
|
128 | 264 | exclude_cot, |
129 | 265 | custom_prompt_name, |
130 | 266 | fine_tune_prompt_id, |
| 267 | + project_id, |
| 268 | + task_id, |
| 269 | + data_requirements_checked, |
| 270 | + has_rated_data, |
| 271 | + has_repair_data, |
131 | 272 | ) |
132 | 273 | } |
133 | 274 | </script> |
|
0 commit comments