Skip to content

Commit dc28a49

Browse files
authored
Merge pull request #1013 from Kiln-AI/leonard/gepa-optimizer-fixes
2 parents 796aa29 + 3f32465 commit dc28a49

File tree

10 files changed

+207
-111
lines changed

10 files changed

+207
-111
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<script lang="ts">
2+
import { goto } from "$app/navigation"
3+
import ConnectKilnCopilotSteps from "./connect_kiln_copilot_steps.svelte"
4+
import AppPage from "../../../routes/(app)/app_page.svelte"
5+
6+
export let title: string
7+
export let docs_link: string
8+
export let breadcrumbs: Array<{ label: string; href: string }>
9+
export let success_redirect_url: string
10+
export let cancel_redirect_url: string
11+
12+
let connect_success = false
13+
14+
function proceed() {
15+
goto(success_redirect_url)
16+
}
17+
18+
function cancel() {
19+
goto(cancel_redirect_url, { replaceState: true })
20+
}
21+
</script>
22+
23+
<div class="max-w-[1400px]">
24+
<AppPage
25+
{title}
26+
sub_subtitle="Read the Docs"
27+
sub_subtitle_link={docs_link}
28+
{breadcrumbs}
29+
>
30+
<div class="flex flex-col max-w-[400px]">
31+
<ConnectKilnCopilotSteps
32+
onSuccess={() => (connect_success = true)}
33+
showCheckmark={connect_success}
34+
/>
35+
{#if connect_success}
36+
<button class="btn btn-primary mt-4 w-full" on:click={proceed}>
37+
Next
38+
</button>
39+
{:else}
40+
<button class="link text-center text-sm mt-8" on:click={cancel}>
41+
Cancel setting up Kiln Copilot
42+
</button>
43+
{/if}
44+
</div>
45+
</AppPage>
46+
</div>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<script lang="ts">
2+
import { goto } from "$app/navigation"
3+
import type { KilnError } from "$lib/utils/error_handlers"
4+
5+
export let description: string
6+
export let auth_url: string
7+
export let back_url: string | null = null
8+
export let back_label: string
9+
export let error: KilnError | null = null
10+
</script>
11+
12+
<div class="max-w-[600px] mx-auto">
13+
<div class="p-8 text-center">
14+
<div class="flex justify-center mb-4">
15+
<img src="/images/animated_logo.svg" alt="Kiln Copilot" class="size-16" />
16+
</div>
17+
<h2 class="text-2xl font-bold mb-3">Kiln Copilot Required</h2>
18+
<p class="text-gray-600 mb-6">{description}</p>
19+
{#if error}
20+
<div class="bg-error/10 border border-error/20 rounded-lg p-3 mb-6">
21+
<div class="text-error text-sm">
22+
{error.getMessage() || "Failed to check Kiln Copilot connection"}
23+
</div>
24+
</div>
25+
{/if}
26+
<div class="flex flex-col gap-3">
27+
<button class="btn btn-primary" on:click={() => goto(auth_url)}>
28+
Connect Kiln Copilot
29+
</button>
30+
{#if back_url}
31+
<a href={back_url} class="link text-sm text-gray-600">
32+
{back_label}
33+
</a>
34+
{/if}
35+
</div>
36+
</div>
37+
</div>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { client } from "$lib/api_client"
2+
3+
/**
4+
* Check if Kiln Copilot is connected (has API key configured)
5+
* @returns true if copilot is available, false otherwise
6+
* @throws Error if the settings API call fails
7+
*/
8+
export async function checkKilnCopilotAvailable(): Promise<boolean> {
9+
const { data, error } = await client.GET("/api/settings")
10+
if (error) {
11+
throw error
12+
}
13+
if (!data) {
14+
throw new Error("Failed to load Kiln settings")
15+
}
16+
return !!data["kiln_copilot_api_key"]
17+
}

app/web_ui/src/routes/(app)/gepa/[project_id]/[task_id]/create_gepa/+page.svelte

Lines changed: 75 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import Output from "$lib/ui/output.svelte"
3232
import Warning from "$lib/ui/warning.svelte"
3333
import TagDropdown from "$lib/ui/tag_dropdown.svelte"
34+
import { checkKilnCopilotAvailable } from "$lib/utils/copilot_utils"
35+
import CopilotRequiredCard from "$lib/ui/kiln_copilot/copilot_required_card.svelte"
3436
3537
function tagFromFilterId(filter_id: string): string | undefined {
3638
if (filter_id.startsWith("tag::")) {
@@ -96,6 +98,8 @@
9698
9799
let current_task: Task | null = null
98100
let task_loading = true
101+
let kiln_copilot_connected: boolean | null = null
102+
let copilot_check_error: KilnError | null = null
99103
100104
type EvalWithConfig = {
101105
eval: Eval
@@ -125,6 +129,11 @@
125129
| "valid"
126130
| "invalid" = "unchecked"
127131
let run_config_validation_message: string | null = null
132+
let run_config_blocking_reason:
133+
| "has_tools"
134+
| "unsupported_model"
135+
| "other"
136+
| null = null
128137
129138
$: has_evals_without_config = evals_with_configs.some(
130139
(item) =>
@@ -207,12 +216,22 @@
207216
onMount(async () => {
208217
from_prompt_generators =
209218
$page.url.searchParams.get("from") === "prompt_generators"
210-
await Promise.all([
211-
load_task(),
212-
load_task_prompts(project_id, task_id),
213-
load_task_run_configs(project_id, task_id),
214-
load_evals_and_configs(),
215-
])
219+
220+
try {
221+
kiln_copilot_connected = await checkKilnCopilotAvailable()
222+
} catch (e) {
223+
copilot_check_error = createKilnError(e)
224+
kiln_copilot_connected = false
225+
}
226+
227+
if (kiln_copilot_connected) {
228+
await Promise.all([
229+
load_task(),
230+
load_task_prompts(project_id, task_id),
231+
load_task_run_configs(project_id, task_id),
232+
load_evals_and_configs(),
233+
])
234+
}
216235
})
217236
218237
async function load_task() {
@@ -326,6 +345,7 @@
326345
try {
327346
run_config_validation_status = "checking"
328347
run_config_validation_message = null
348+
run_config_blocking_reason = null
329349
330350
const run_config = get_selected_run_config(
331351
target_run_config_id,
@@ -341,6 +361,7 @@
341361
run_config_validation_status = "invalid"
342362
run_config_validation_message =
343363
"Tools are not supported for Kiln Prompt Optimization"
364+
run_config_blocking_reason = "has_tools"
344365
return
345366
}
346367
@@ -365,6 +386,7 @@
365386
366387
if (data && !data.is_supported) {
367388
run_config_validation_status = "invalid"
389+
run_config_blocking_reason = "unsupported_model"
368390
if (run_config) {
369391
const friendly_model = model_name(
370392
run_config.run_config_properties.model_name,
@@ -381,10 +403,12 @@
381403
} else {
382404
run_config_validation_status = "valid"
383405
run_config_validation_message = null
406+
run_config_blocking_reason = null
384407
}
385408
} catch (e) {
386409
run_config_validation_status = "invalid"
387410
run_config_validation_message = createKilnError(e).getMessage()
411+
run_config_blocking_reason = "other"
388412
}
389413
}
390414
@@ -578,6 +602,7 @@
578602
} else {
579603
run_config_validation_status = "unchecked"
580604
run_config_validation_message = null
605+
run_config_blocking_reason = null
581606
}
582607
583608
$: if (evals_with_configs.length > 0 && !evals_loading) {
@@ -593,15 +618,6 @@
593618
})
594619
}
595620
596-
onMount(async () => {
597-
await Promise.all([
598-
load_task(),
599-
load_task_prompts(project_id, task_id),
600-
load_task_run_configs(project_id, task_id),
601-
load_evals_and_configs(),
602-
])
603-
})
604-
605621
async function refresh_evaluators() {
606622
const { data: evals_data } = await client.GET(
607623
"/api/projects/{project_id}/tasks/{task_id}/evals",
@@ -762,7 +778,18 @@
762778
},
763779
]}
764780
>
765-
{#if task_loading}
781+
{#if kiln_copilot_connected === null}
782+
<div class="w-full min-h-[50vh] flex justify-center items-center">
783+
<div class="loading loading-spinner loading-lg"></div>
784+
</div>
785+
{:else if kiln_copilot_connected === false}
786+
<CopilotRequiredCard
787+
description="Kiln Prompt Optimization requires Kiln Copilot to automatically optimize your prompts using advanced techniques."
788+
auth_url="/gepa/copilot_auth"
789+
back_label="Back to Optimizer Jobs"
790+
error={copilot_check_error}
791+
/>
792+
{:else if task_loading}
766793
<div class="w-full min-h-[50vh] flex justify-center items-center">
767794
<div class="loading loading-spinner loading-lg"></div>
768795
</div>
@@ -866,38 +893,40 @@
866893
<div class="mt-3">
867894
<Warning warning_color="error" outline={true}>
868895
<div>
869-
<div class="text-error font-medium mb-2">
896+
<div class="text-error font-medium">
870897
{run_config_validation_message}
871898
</div>
872-
<div class="text-gray-600">
873-
{#if run_config_validation_message?.includes("Tools")}
874-
Kiln Prompt Optimization does not support run
875-
configurations that use tools. Please select a
876-
different run configuration or
877-
<button
878-
type="button"
879-
class="link underline"
880-
on:click={() =>
881-
create_new_run_config_dialog?.show()}
882-
>
883-
create a new one
884-
</button>
885-
without tools configured.
886-
{:else}
887-
Kiln Prompt Optimization only supports OpenRouter,
888-
OpenAI, Gemini, and Anthropic providers. Please select
889-
a different run configuration or
890-
<button
891-
type="button"
892-
class="link underline"
893-
on:click={() =>
894-
create_new_run_config_dialog?.show()}
895-
>
896-
create a new one
897-
</button>
898-
with a supported provider.
899-
{/if}
900-
</div>
899+
{#if run_config_blocking_reason !== "other"}
900+
<div class="mt-2 text-gray-600">
901+
{#if run_config_blocking_reason === "has_tools"}
902+
Kiln Prompt Optimization does not support run
903+
configurations that use tools. Please select a
904+
different run configuration or
905+
<button
906+
type="button"
907+
class="link underline"
908+
on:click={() =>
909+
create_new_run_config_dialog?.show()}
910+
>
911+
create a new one
912+
</button>
913+
without tools configured.
914+
{:else if run_config_blocking_reason === "unsupported_model"}
915+
Kiln Prompt Optimization only supports OpenRouter,
916+
OpenAI, Gemini, and Anthropic providers. Please
917+
select a different run configuration or
918+
<button
919+
type="button"
920+
class="link underline"
921+
on:click={() =>
922+
create_new_run_config_dialog?.show()}
923+
>
924+
create a new one
925+
</button>
926+
with a supported provider.
927+
{/if}
928+
</div>
929+
{/if}
901930
</div>
902931
</Warning>
903932
</div>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script lang="ts">
2+
import { ui_state } from "$lib/stores"
3+
import CopilotAuthPage from "$lib/ui/kiln_copilot/copilot_auth_page.svelte"
4+
5+
$: project_id = $ui_state.current_project_id
6+
$: task_id = $ui_state.current_task_id
7+
</script>
8+
9+
<CopilotAuthPage
10+
title="Create Optimized Prompt"
11+
docs_link="https://docs.kiln.tech/docs/prompts"
12+
breadcrumbs={[
13+
{ label: "Optimize", href: `/optimize/${project_id}/${task_id}` },
14+
{ label: "Prompts", href: `/prompts/${project_id}/${task_id}` },
15+
{ label: "Optimizer Jobs", href: `/gepa/${project_id}/${task_id}` },
16+
]}
17+
success_redirect_url={`/gepa/${project_id}/${task_id}/create_gepa`}
18+
cancel_redirect_url={`/gepa/${project_id}/${task_id}/create_gepa`}
19+
/>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const ssr = false

app/web_ui/src/routes/(app)/specs/[project_id]/[task_id]/+page.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
import {
2020
updateSpecPriority as updateSpecPriorityUtil,
2121
updateSpecStatus as updateSpecStatusUtil,
22-
checkKilnCopilotAvailable,
2322
} from "./spec_utils"
23+
import { checkKilnCopilotAvailable } from "$lib/utils/copilot_utils"
2424
import EvalIcon from "$lib/ui/icons/eval_icon.svelte"
2525
import posthog from "posthog-js"
2626

app/web_ui/src/routes/(app)/specs/[project_id]/[task_id]/spec_builder/+page.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
import { goto } from "$app/navigation"
2020
import { spec_field_configs } from "../select_template/spec_templates"
2121
import {
22-
checkKilnCopilotAvailable,
2322
checkDefaultRunConfigHasTools,
2423
buildSpecDefinition,
2524
type SuggestedEdit,
2625
type ReviewRow,
2726
} from "../spec_utils"
27+
import { checkKilnCopilotAvailable } from "$lib/utils/copilot_utils"
2828
import { client } from "$lib/api_client"
2929
import {
3030
load_task,

app/web_ui/src/routes/(app)/specs/[project_id]/[task_id]/spec_utils.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,6 @@ export function buildSpecDefinition(
5757
return parts.join("\n\n")
5858
}
5959

60-
/**
61-
* Check if Kiln Copilot is connected (has API key configured)
62-
* @returns true if copilot is available, false otherwise
63-
* @throws Error if the settings API call fails
64-
*/
65-
export async function checkKilnCopilotAvailable(): Promise<boolean> {
66-
const { data, error } = await client.GET("/api/settings")
67-
if (error) {
68-
throw error
69-
}
70-
if (!data) {
71-
throw new Error("Failed to load Kiln settings")
72-
}
73-
return !!data["kiln_copilot_api_key"]
74-
}
75-
7660
/**
7761
* Check if the task's default run config has any tools configured
7862
* @param project_id - The project ID

0 commit comments

Comments
 (0)