From a3bd11e707b2beb8a2d920dddefdebbf389516f7 Mon Sep 17 00:00:00 2001 From: "Leonard Q. Marcq" Date: Mon, 16 Feb 2026 17:42:51 +0800 Subject: [PATCH 1/2] refactor: rename gepa into prompt optimization --- app/desktop/desktop_server.py | 6 +- ...mization_job_check_model_supported_get.py} | 10 +- ...mpt_optimization_job_job_id_result_get.py} | 34 +- ...obs_prompt_optimization_job_start_post.py} | 32 +- .../kiln_ai_server_client/models/__init__.py | 14 +- ...obs_prompt_optimization_job_start_post.py} | 10 +- ...t.py => prompt_optimization_job_output.py} | 12 +- ...rompt_optimization_job_result_response.py} | 30 +- ..._api.py => prompt_optimization_job_api.py} | 317 +++--- ...py => test_prompt_optimization_job_api.py} | 1001 ++++++++++------- app/web_ui/src/lib/api_schema.d.ts | 238 ++-- app/web_ui/src/lib/types.ts | 7 +- .../kiln_copilot/copilot_required_card.svelte | 2 +- .../[project_id]/[task_id]/+page.svelte | 87 +- .../[project_id]/[task_id]/+page.ts | 0 .../+page.svelte | 12 +- .../create_prompt_optimization_job}/+page.ts | 0 .../[job_id]/+page.svelte | 129 ++- .../[job_id]/+page.ts | 0 .../copilot_auth/+page.svelte | 4 +- .../copilot_auth/+page.ts | 0 .../[project_id]/[task_id]/+page.svelte | 4 +- .../[task_id]/prompt_generators/+page.svelte | 4 +- libs/core/kiln_ai/datamodel/__init__.py | 4 +- ...gepa_job.py => prompt_optimization_job.py} | 10 +- libs/core/kiln_ai/datamodel/task.py | 10 +- ...job.py => test_prompt_optimization_job.py} | 86 +- libs/core/kiln_ai/datamodel/test_task.py | 48 +- 28 files changed, 1170 insertions(+), 941 deletions(-) rename app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/{check_model_supported_v1_jobs_gepa_job_check_model_supported_get.py => check_prompt_optimization_model_supported_v1_jobs_prompt_optimization_job_check_model_supported_get.py} (94%) rename app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/{get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.py => get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.py} (73%) rename app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/{start_gepa_job_v1_jobs_gepa_job_start_post.py => start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.py} (77%) rename app/desktop/studio_server/api_client/kiln_ai_server_client/models/{body_start_gepa_job_v1_jobs_gepa_job_start_post.py => body_start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.py} (86%) rename app/desktop/studio_server/api_client/kiln_ai_server_client/models/{gepa_job_output.py => prompt_optimization_job_output.py} (82%) rename app/desktop/studio_server/api_client/kiln_ai_server_client/models/{gepa_job_result_response.py => prompt_optimization_job_result_response.py} (76%) rename app/desktop/studio_server/{gepa_job_api.py => prompt_optimization_job_api.py} (64%) rename app/desktop/studio_server/{test_gepa_job_api.py => test_prompt_optimization_job_api.py} (68%) rename app/web_ui/src/routes/(app)/{gepa => prompt_optimization}/[project_id]/[task_id]/+page.svelte (69%) rename app/web_ui/src/routes/(app)/{gepa => prompt_optimization}/[project_id]/[task_id]/+page.ts (100%) rename app/web_ui/src/routes/(app)/{gepa/[project_id]/[task_id]/create_gepa => prompt_optimization/[project_id]/[task_id]/create_prompt_optimization_job}/+page.svelte (98%) rename app/web_ui/src/routes/(app)/{gepa/[project_id]/[task_id]/create_gepa => prompt_optimization/[project_id]/[task_id]/create_prompt_optimization_job}/+page.ts (100%) rename app/web_ui/src/routes/(app)/{gepa/[project_id]/[task_id]/gepa_job => prompt_optimization/[project_id]/[task_id]/prompt_optimization_job}/[job_id]/+page.svelte (62%) rename app/web_ui/src/routes/(app)/{gepa/[project_id]/[task_id]/gepa_job => prompt_optimization/[project_id]/[task_id]/prompt_optimization_job}/[job_id]/+page.ts (100%) rename app/web_ui/src/routes/(app)/{gepa => prompt_optimization}/copilot_auth/+page.svelte (70%) rename app/web_ui/src/routes/(app)/{gepa => prompt_optimization}/copilot_auth/+page.ts (100%) rename libs/core/kiln_ai/datamodel/{gepa_job.py => prompt_optimization_job.py} (76%) rename libs/core/kiln_ai/datamodel/{test_gepa_job.py => test_prompt_optimization_job.py} (54%) diff --git a/app/desktop/desktop_server.py b/app/desktop/desktop_server.py index c47ec8fd7..493d58d9f 100644 --- a/app/desktop/desktop_server.py +++ b/app/desktop/desktop_server.py @@ -19,7 +19,9 @@ from app.desktop.studio_server.dev_tools import connect_dev_tools from app.desktop.studio_server.eval_api import connect_evals_api from app.desktop.studio_server.finetune_api import connect_fine_tune_api -from app.desktop.studio_server.gepa_job_api import connect_gepa_job_api +from app.desktop.studio_server.prompt_optimization_job_api import ( + connect_prompt_optimization_job_api, +) from app.desktop.studio_server.import_api import connect_import_api from app.desktop.studio_server.prompt_api import connect_prompt_api from app.desktop.studio_server.provider_api import connect_provider_api @@ -66,7 +68,7 @@ def make_app(tk_root: tk.Tk | None = None): connect_evals_api(app) connect_import_api(app, tk_root=tk_root) connect_tool_servers_api(app) - connect_gepa_job_api(app) + connect_prompt_optimization_job_api(app) connect_copilot_api(app) connect_dev_tools(app) diff --git a/app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/check_model_supported_v1_jobs_gepa_job_check_model_supported_get.py b/app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/check_prompt_optimization_model_supported_v1_jobs_prompt_optimization_job_check_model_supported_get.py similarity index 94% rename from app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/check_model_supported_v1_jobs_gepa_job_check_model_supported_get.py rename to app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/check_prompt_optimization_model_supported_v1_jobs_prompt_optimization_job_check_model_supported_get.py index 6c3303b7b..197c8cf5f 100644 --- a/app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/check_model_supported_v1_jobs_gepa_job_check_model_supported_get.py +++ b/app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/check_prompt_optimization_model_supported_v1_jobs_prompt_optimization_job_check_model_supported_get.py @@ -26,7 +26,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": "/v1/jobs/gepa_job/check_model_supported", + "url": "/v1/jobs/prompt_optimization_job/check_model_supported", "params": params, } @@ -69,7 +69,7 @@ def sync_detailed( model_name: str, model_provider_name: str, ) -> Response[CheckModelSupportedResponse | HTTPValidationError]: - """Check Model Supported + """Check Prompt Optimization Model Supported Args: model_name (str): @@ -101,7 +101,7 @@ def sync( model_name: str, model_provider_name: str, ) -> CheckModelSupportedResponse | HTTPValidationError | None: - """Check Model Supported + """Check Prompt Optimization Model Supported Args: model_name (str): @@ -128,7 +128,7 @@ async def asyncio_detailed( model_name: str, model_provider_name: str, ) -> Response[CheckModelSupportedResponse | HTTPValidationError]: - """Check Model Supported + """Check Prompt Optimization Model Supported Args: model_name (str): @@ -158,7 +158,7 @@ async def asyncio( model_name: str, model_provider_name: str, ) -> CheckModelSupportedResponse | HTTPValidationError | None: - """Check Model Supported + """Check Prompt Optimization Model Supported Args: model_name (str): diff --git a/app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.py b/app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.py similarity index 73% rename from app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.py rename to app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.py index 8863bf517..3a7e9223a 100644 --- a/app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.py +++ b/app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.py @@ -6,8 +6,8 @@ from ... import errors from ...client import AuthenticatedClient, Client -from ...models.gepa_job_result_response import GEPAJobResultResponse from ...models.http_validation_error import HTTPValidationError +from ...models.prompt_optimization_job_result_response import PromptOptimizationJobResultResponse from ...types import Response @@ -17,7 +17,7 @@ def _get_kwargs( _kwargs: dict[str, Any] = { "method": "get", - "url": "/v1/jobs/gepa_job/{job_id}/result".format( + "url": "/v1/jobs/prompt_optimization_job/{job_id}/result".format( job_id=quote(str(job_id), safe=""), ), } @@ -27,9 +27,9 @@ def _get_kwargs( def _parse_response( *, client: AuthenticatedClient | Client, response: httpx.Response -) -> GEPAJobResultResponse | HTTPValidationError | None: +) -> HTTPValidationError | PromptOptimizationJobResultResponse | None: if response.status_code == 200: - response_200 = GEPAJobResultResponse.from_dict(response.json()) + response_200 = PromptOptimizationJobResultResponse.from_dict(response.json()) return response_200 @@ -46,7 +46,7 @@ def _parse_response( def _build_response( *, client: AuthenticatedClient | Client, response: httpx.Response -) -> Response[GEPAJobResultResponse | HTTPValidationError]: +) -> Response[HTTPValidationError | PromptOptimizationJobResultResponse]: return Response( status_code=HTTPStatus(response.status_code), content=response.content, @@ -59,8 +59,8 @@ def sync_detailed( job_id: str, *, client: AuthenticatedClient, -) -> Response[GEPAJobResultResponse | HTTPValidationError]: - """Get Gepa Job Result +) -> Response[HTTPValidationError | PromptOptimizationJobResultResponse]: + """Get Prompt Optimization Job Result Args: job_id (str): @@ -70,7 +70,7 @@ def sync_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[GEPAJobResultResponse | HTTPValidationError] + Response[HTTPValidationError | PromptOptimizationJobResultResponse] """ kwargs = _get_kwargs( @@ -88,8 +88,8 @@ def sync( job_id: str, *, client: AuthenticatedClient, -) -> GEPAJobResultResponse | HTTPValidationError | None: - """Get Gepa Job Result +) -> HTTPValidationError | PromptOptimizationJobResultResponse | None: + """Get Prompt Optimization Job Result Args: job_id (str): @@ -99,7 +99,7 @@ def sync( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - GEPAJobResultResponse | HTTPValidationError + HTTPValidationError | PromptOptimizationJobResultResponse """ return sync_detailed( @@ -112,8 +112,8 @@ async def asyncio_detailed( job_id: str, *, client: AuthenticatedClient, -) -> Response[GEPAJobResultResponse | HTTPValidationError]: - """Get Gepa Job Result +) -> Response[HTTPValidationError | PromptOptimizationJobResultResponse]: + """Get Prompt Optimization Job Result Args: job_id (str): @@ -123,7 +123,7 @@ async def asyncio_detailed( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - Response[GEPAJobResultResponse | HTTPValidationError] + Response[HTTPValidationError | PromptOptimizationJobResultResponse] """ kwargs = _get_kwargs( @@ -139,8 +139,8 @@ async def asyncio( job_id: str, *, client: AuthenticatedClient, -) -> GEPAJobResultResponse | HTTPValidationError | None: - """Get Gepa Job Result +) -> HTTPValidationError | PromptOptimizationJobResultResponse | None: + """Get Prompt Optimization Job Result Args: job_id (str): @@ -150,7 +150,7 @@ async def asyncio( httpx.TimeoutException: If the request takes longer than Client.timeout. Returns: - GEPAJobResultResponse | HTTPValidationError + HTTPValidationError | PromptOptimizationJobResultResponse """ return ( diff --git a/app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/start_gepa_job_v1_jobs_gepa_job_start_post.py b/app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.py similarity index 77% rename from app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/start_gepa_job_v1_jobs_gepa_job_start_post.py rename to app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.py index f29e81cb0..8b282ed87 100644 --- a/app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/start_gepa_job_v1_jobs_gepa_job_start_post.py +++ b/app/desktop/studio_server/api_client/kiln_ai_server_client/api/jobs/start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.py @@ -5,7 +5,9 @@ from ... import errors from ...client import AuthenticatedClient, Client -from ...models.body_start_gepa_job_v1_jobs_gepa_job_start_post import BodyStartGepaJobV1JobsGepaJobStartPost +from ...models.body_start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post import ( + BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost, +) from ...models.http_validation_error import HTTPValidationError from ...models.job_start_response import JobStartResponse from ...types import Response @@ -13,13 +15,13 @@ def _get_kwargs( *, - body: BodyStartGepaJobV1JobsGepaJobStartPost, + body: BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost, ) -> dict[str, Any]: headers: dict[str, Any] = {} _kwargs: dict[str, Any] = { "method": "post", - "url": "/v1/jobs/gepa_job/start", + "url": "/v1/jobs/prompt_optimization_job/start", } _kwargs["files"] = body.to_multipart() @@ -61,12 +63,12 @@ def _build_response( def sync_detailed( *, client: AuthenticatedClient, - body: BodyStartGepaJobV1JobsGepaJobStartPost, + body: BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost, ) -> Response[HTTPValidationError | JobStartResponse]: - """Start Gepa Job + """Start Prompt Optimization Job Args: - body (BodyStartGepaJobV1JobsGepaJobStartPost): + body (BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -90,12 +92,12 @@ def sync_detailed( def sync( *, client: AuthenticatedClient, - body: BodyStartGepaJobV1JobsGepaJobStartPost, + body: BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost, ) -> HTTPValidationError | JobStartResponse | None: - """Start Gepa Job + """Start Prompt Optimization Job Args: - body (BodyStartGepaJobV1JobsGepaJobStartPost): + body (BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -114,12 +116,12 @@ def sync( async def asyncio_detailed( *, client: AuthenticatedClient, - body: BodyStartGepaJobV1JobsGepaJobStartPost, + body: BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost, ) -> Response[HTTPValidationError | JobStartResponse]: - """Start Gepa Job + """Start Prompt Optimization Job Args: - body (BodyStartGepaJobV1JobsGepaJobStartPost): + body (BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -141,12 +143,12 @@ async def asyncio_detailed( async def asyncio( *, client: AuthenticatedClient, - body: BodyStartGepaJobV1JobsGepaJobStartPost, + body: BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost, ) -> HTTPValidationError | JobStartResponse | None: - """Start Gepa Job + """Start Prompt Optimization Job Args: - body (BodyStartGepaJobV1JobsGepaJobStartPost): + body (BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. diff --git a/app/desktop/studio_server/api_client/kiln_ai_server_client/models/__init__.py b/app/desktop/studio_server/api_client/kiln_ai_server_client/models/__init__.py index c830ce6da..d793ae480 100644 --- a/app/desktop/studio_server/api_client/kiln_ai_server_client/models/__init__.py +++ b/app/desktop/studio_server/api_client/kiln_ai_server_client/models/__init__.py @@ -3,7 +3,9 @@ from .answer_option import AnswerOption from .answer_option_with_selection import AnswerOptionWithSelection from .api_key_verification_result import ApiKeyVerificationResult -from .body_start_gepa_job_v1_jobs_gepa_job_start_post import BodyStartGepaJobV1JobsGepaJobStartPost +from .body_start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post import ( + BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost, +) from .body_start_sample_job_v1_jobs_sample_job_start_post import BodyStartSampleJobV1JobsSampleJobStartPost from .check_entitlements_v1_check_entitlements_get_response_check_entitlements_v1_check_entitlements_get import ( CheckEntitlementsV1CheckEntitlementsGetResponseCheckEntitlementsV1CheckEntitlementsGet, @@ -16,8 +18,6 @@ from .generate_batch_input import GenerateBatchInput from .generate_batch_output import GenerateBatchOutput from .generate_batch_output_data_by_topic import GenerateBatchOutputDataByTopic -from .gepa_job_output import GEPAJobOutput -from .gepa_job_result_response import GEPAJobResultResponse from .health_health_get_response_health_health_get import HealthHealthGetResponseHealthHealthGet from .http_validation_error import HTTPValidationError from .job_start_response import JobStartResponse @@ -27,6 +27,8 @@ from .model_provider_name import ModelProviderName from .new_proposed_spec_edit_api import NewProposedSpecEditApi from .output_file_info import OutputFileInfo +from .prompt_optimization_job_output import PromptOptimizationJobOutput +from .prompt_optimization_job_result_response import PromptOptimizationJobResultResponse from .question import Question from .question_set import QuestionSet from .question_with_answer import QuestionWithAnswer @@ -55,7 +57,7 @@ "AnswerOption", "AnswerOptionWithSelection", "ApiKeyVerificationResult", - "BodyStartGepaJobV1JobsGepaJobStartPost", + "BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost", "BodyStartSampleJobV1JobsSampleJobStartPost", "CheckEntitlementsV1CheckEntitlementsGetResponseCheckEntitlementsV1CheckEntitlementsGet", "CheckModelSupportedResponse", @@ -66,8 +68,6 @@ "GenerateBatchInput", "GenerateBatchOutput", "GenerateBatchOutputDataByTopic", - "GEPAJobOutput", - "GEPAJobResultResponse", "HealthHealthGetResponseHealthHealthGet", "HTTPValidationError", "JobStartResponse", @@ -77,6 +77,8 @@ "ModelProviderName", "NewProposedSpecEditApi", "OutputFileInfo", + "PromptOptimizationJobOutput", + "PromptOptimizationJobResultResponse", "Question", "QuestionSet", "QuestionWithAnswer", diff --git a/app/desktop/studio_server/api_client/kiln_ai_server_client/models/body_start_gepa_job_v1_jobs_gepa_job_start_post.py b/app/desktop/studio_server/api_client/kiln_ai_server_client/models/body_start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.py similarity index 86% rename from app/desktop/studio_server/api_client/kiln_ai_server_client/models/body_start_gepa_job_v1_jobs_gepa_job_start_post.py rename to app/desktop/studio_server/api_client/kiln_ai_server_client/models/body_start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.py index d2dc91f0d..7a611bbd9 100644 --- a/app/desktop/studio_server/api_client/kiln_ai_server_client/models/body_start_gepa_job_v1_jobs_gepa_job_start_post.py +++ b/app/desktop/studio_server/api_client/kiln_ai_server_client/models/body_start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.py @@ -10,11 +10,11 @@ from .. import types from ..types import File -T = TypeVar("T", bound="BodyStartGepaJobV1JobsGepaJobStartPost") +T = TypeVar("T", bound="BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost") @_attrs_define -class BodyStartGepaJobV1JobsGepaJobStartPost: +class BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost: """ Attributes: task_id (str): The task ID @@ -79,15 +79,15 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: project_zip = File(payload=BytesIO(d.pop("project_zip"))) - body_start_gepa_job_v1_jobs_gepa_job_start_post = cls( + body_start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post = cls( task_id=task_id, target_run_config_id=target_run_config_id, eval_ids=eval_ids, project_zip=project_zip, ) - body_start_gepa_job_v1_jobs_gepa_job_start_post.additional_properties = d - return body_start_gepa_job_v1_jobs_gepa_job_start_post + body_start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.additional_properties = d + return body_start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post @property def additional_keys(self) -> list[str]: diff --git a/app/desktop/studio_server/api_client/kiln_ai_server_client/models/gepa_job_output.py b/app/desktop/studio_server/api_client/kiln_ai_server_client/models/prompt_optimization_job_output.py similarity index 82% rename from app/desktop/studio_server/api_client/kiln_ai_server_client/models/gepa_job_output.py rename to app/desktop/studio_server/api_client/kiln_ai_server_client/models/prompt_optimization_job_output.py index 50b0cd6fd..e96411aa8 100644 --- a/app/desktop/studio_server/api_client/kiln_ai_server_client/models/gepa_job_output.py +++ b/app/desktop/studio_server/api_client/kiln_ai_server_client/models/prompt_optimization_job_output.py @@ -6,12 +6,12 @@ from attrs import define as _attrs_define from attrs import field as _attrs_field -T = TypeVar("T", bound="GEPAJobOutput") +T = TypeVar("T", bound="PromptOptimizationJobOutput") @_attrs_define -class GEPAJobOutput: - """Output from the GEPA job. +class PromptOptimizationJobOutput: + """Output from the prompt optimization job. Attributes: optimized_prompt (str): @@ -38,12 +38,12 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: d = dict(src_dict) optimized_prompt = d.pop("optimized_prompt") - gepa_job_output = cls( + prompt_optimization_job_output = cls( optimized_prompt=optimized_prompt, ) - gepa_job_output.additional_properties = d - return gepa_job_output + prompt_optimization_job_output.additional_properties = d + return prompt_optimization_job_output @property def additional_keys(self) -> list[str]: diff --git a/app/desktop/studio_server/api_client/kiln_ai_server_client/models/gepa_job_result_response.py b/app/desktop/studio_server/api_client/kiln_ai_server_client/models/prompt_optimization_job_result_response.py similarity index 76% rename from app/desktop/studio_server/api_client/kiln_ai_server_client/models/gepa_job_result_response.py rename to app/desktop/studio_server/api_client/kiln_ai_server_client/models/prompt_optimization_job_result_response.py index 2e2a9038c..92077b56b 100644 --- a/app/desktop/studio_server/api_client/kiln_ai_server_client/models/gepa_job_result_response.py +++ b/app/desktop/studio_server/api_client/kiln_ai_server_client/models/prompt_optimization_job_result_response.py @@ -10,37 +10,37 @@ from ..types import UNSET, Unset if TYPE_CHECKING: - from ..models.gepa_job_output import GEPAJobOutput from ..models.output_file_info import OutputFileInfo + from ..models.prompt_optimization_job_output import PromptOptimizationJobOutput -T = TypeVar("T", bound="GEPAJobResultResponse") +T = TypeVar("T", bound="PromptOptimizationJobResultResponse") @_attrs_define -class GEPAJobResultResponse: - """Response model for GEPA job result. +class PromptOptimizationJobResultResponse: + """Response model for prompt optimization job result. Attributes: status (JobStatus): Job execution status aligned with Google Cloud Run Job execution states. - output (GEPAJobOutput | None | Unset): + output (None | PromptOptimizationJobOutput | Unset): output_files (list[OutputFileInfo] | Unset): """ status: JobStatus - output: GEPAJobOutput | None | Unset = UNSET + output: None | PromptOptimizationJobOutput | Unset = UNSET output_files: list[OutputFileInfo] | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: - from ..models.gepa_job_output import GEPAJobOutput + from ..models.prompt_optimization_job_output import PromptOptimizationJobOutput status = self.status.value output: dict[str, Any] | None | Unset if isinstance(self.output, Unset): output = UNSET - elif isinstance(self.output, GEPAJobOutput): + elif isinstance(self.output, PromptOptimizationJobOutput): output = self.output.to_dict() else: output = self.output @@ -68,13 +68,13 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: - from ..models.gepa_job_output import GEPAJobOutput from ..models.output_file_info import OutputFileInfo + from ..models.prompt_optimization_job_output import PromptOptimizationJobOutput d = dict(src_dict) status = JobStatus(d.pop("status")) - def _parse_output(data: object) -> GEPAJobOutput | None | Unset: + def _parse_output(data: object) -> None | PromptOptimizationJobOutput | Unset: if data is None: return data if isinstance(data, Unset): @@ -82,12 +82,12 @@ def _parse_output(data: object) -> GEPAJobOutput | None | Unset: try: if not isinstance(data, dict): raise TypeError() - output_type_0 = GEPAJobOutput.from_dict(data) + output_type_0 = PromptOptimizationJobOutput.from_dict(data) return output_type_0 except (TypeError, ValueError, AttributeError, KeyError): pass - return cast(GEPAJobOutput | None | Unset, data) + return cast(None | PromptOptimizationJobOutput | Unset, data) output = _parse_output(d.pop("output", UNSET)) @@ -100,14 +100,14 @@ def _parse_output(data: object) -> GEPAJobOutput | None | Unset: output_files.append(output_files_item) - gepa_job_result_response = cls( + prompt_optimization_job_result_response = cls( status=status, output=output, output_files=output_files, ) - gepa_job_result_response.additional_properties = d - return gepa_job_result_response + prompt_optimization_job_result_response.additional_properties = d + return prompt_optimization_job_result_response @property def additional_keys(self) -> list[str]: diff --git a/app/desktop/studio_server/gepa_job_api.py b/app/desktop/studio_server/prompt_optimization_job_api.py similarity index 64% rename from app/desktop/studio_server/gepa_job_api.py rename to app/desktop/studio_server/prompt_optimization_job_api.py index 5d0060a17..937956abd 100644 --- a/app/desktop/studio_server/gepa_job_api.py +++ b/app/desktop/studio_server/prompt_optimization_job_api.py @@ -5,16 +5,16 @@ from pathlib import Path from app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs import ( - check_model_supported_v1_jobs_gepa_job_check_model_supported_get, - get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get, + check_prompt_optimization_model_supported_v1_jobs_prompt_optimization_job_check_model_supported_get, get_job_status_v1_jobs_job_type_job_id_status_get, - start_gepa_job_v1_jobs_gepa_job_start_post, + get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get, + start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post, ) from app.desktop.studio_server.api_client.kiln_ai_server_client.client import ( AuthenticatedClient, ) -from app.desktop.studio_server.api_client.kiln_ai_server_client.models.body_start_gepa_job_v1_jobs_gepa_job_start_post import ( - BodyStartGepaJobV1JobsGepaJobStartPost, +from app.desktop.studio_server.api_client.kiln_ai_server_client.models.body_start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post import ( + BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost, ) from app.desktop.studio_server.api_client.kiln_ai_server_client.models.http_validation_error import ( HTTPValidationError, @@ -40,7 +40,7 @@ PackageForTrainingConfig, package_project_for_training, ) -from kiln_ai.datamodel import GepaJob, Prompt +from kiln_ai.datamodel import Prompt, PromptOptimizationJob from kiln_ai.datamodel.task import TaskRunConfig from kiln_ai.utils.config import Config from kiln_ai.utils.lock import shared_async_lock_manager @@ -63,14 +63,14 @@ def is_job_status_final(status: str) -> bool: ] -class PublicGEPAJobResultResponse(BaseModel): - """Public response model for GEPA job result containing only the optimized prompt.""" +class PublicPromptOptimizationJobResultResponse(BaseModel): + """Public response model for prompt optimization job result containing only the optimized prompt.""" optimized_prompt: str -class PublicGEPAJobStatusResponse(BaseModel): - """Public response model for GEPA job status.""" +class PublicPromptOptimizationJobStatusResponse(BaseModel): + """Public response model for prompt optimization job status.""" job_id: str status: JobStatus @@ -101,32 +101,36 @@ def _get_api_key() -> str: return api_key -class StartGepaJobRequest(BaseModel): +class StartPromptOptimizationJobRequest(BaseModel): target_run_config_id: str eval_ids: list[str] -def gepa_job_from_id(project_id: str, task_id: str, gepa_job_id: str) -> GepaJob: - """Get a GepaJob from its ID, raising HTTPException if not found.""" +def prompt_optimization_job_from_id( + project_id: str, task_id: str, prompt_optimization_job_id: str +) -> PromptOptimizationJob: + """Get a PromptOptimizationJob from its ID, raising HTTPException if not found.""" task = task_from_id(project_id, task_id) - gepa_job = GepaJob.from_id_and_parent_path(gepa_job_id, task.path) - if gepa_job is None: + prompt_optimization_job = PromptOptimizationJob.from_id_and_parent_path( + prompt_optimization_job_id, task.path + ) + if prompt_optimization_job is None: raise HTTPException( status_code=404, - detail=f"GEPA job with ID '{gepa_job_id}' not found", + detail=f"Prompt Optimization job with ID '{prompt_optimization_job_id}' not found", ) - return gepa_job + return prompt_optimization_job def create_prompt_from_optimization( - gepa_job: GepaJob, task, optimized_prompt_text: str + prompt_optimization_job: PromptOptimizationJob, task, optimized_prompt_text: str ) -> Prompt: """ Create a prompt from an optimization job result. Does not guarantee idempotence so make sure you have a proper locking mechanism around calling this function. """ prompt = Prompt( - name=gepa_job.name, + name=prompt_optimization_job.name, generator_id="kiln_prompt_optimizer", prompt=optimized_prompt_text, parent=task, @@ -137,7 +141,7 @@ def create_prompt_from_optimization( def create_run_config_from_optimization( - gepa_job: GepaJob, task, prompt: Prompt + prompt_optimization_job: PromptOptimizationJob, task, prompt: Prompt ) -> TaskRunConfig: """ Create a run config from an optimization job result. Does not guarantee idempotence so @@ -158,7 +162,7 @@ def create_run_config_from_optimization( # get the original target run config that we optimized for in the job target_run_config = task_run_config_from_id( - parent_project.id, task.id, gepa_job.target_run_config_id + parent_project.id, task.id, prompt_optimization_job.target_run_config_id ) # create new run config with the same properties but new prompt @@ -180,7 +184,9 @@ def create_run_config_from_optimization( def _cleanup_artifact( - artifact: Prompt | TaskRunConfig | None, artifact_type: str, gepa_job_id: str + artifact: Prompt | TaskRunConfig | None, + artifact_type: str, + prompt_optimization_job_id: str, ) -> None: """ Attempt to delete an artifact, logging errors if deletion fails. @@ -195,43 +201,48 @@ def _cleanup_artifact( artifact_id = getattr(artifact, "id", "unknown") logger.error( f"Failed to clean up {artifact_type} artifact {artifact_id} " - f"for GEPA job {gepa_job_id}: {cleanup_error}", + f"for Prompt Optimization job {prompt_optimization_job_id}: {cleanup_error}", exc_info=True, ) async def _create_artifacts_for_succeeded_job( - gepa_job: GepaJob, + prompt_optimization_job: PromptOptimizationJob, task, server_client: AuthenticatedClient, ) -> None: """ - Create prompt and run config artifacts for a newly succeeded GEPA job. - Assumes caller has acquired the job lock. Modifies gepa_job in place. + Create prompt and run config artifacts for a newly succeeded prompt optimization job. + Assumes caller has acquired the job lock. Modifies prompt_optimization_job in place. """ parent_project = task.parent_project() - if not parent_project or not parent_project.id or not task.id or not gepa_job.id: - raise ValueError("Cannot reload GEPA job: missing required IDs") + if ( + not parent_project + or not parent_project.id + or not task.id + or not prompt_optimization_job.id + ): + raise ValueError("Cannot reload Prompt Optimization job: missing required IDs") # reload the job in case artifacts were created by another request while waiting for the lock - reloaded_job = gepa_job_from_id( + reloaded_job = prompt_optimization_job_from_id( parent_project.id, task.id, - gepa_job.id, + prompt_optimization_job.id, ) # check if artifacts already exist if reloaded_job.created_prompt_id: - gepa_job.created_prompt_id = reloaded_job.created_prompt_id - gepa_job.created_run_config_id = reloaded_job.created_run_config_id - gepa_job.optimized_prompt = reloaded_job.optimized_prompt + prompt_optimization_job.created_prompt_id = reloaded_job.created_prompt_id + prompt_optimization_job.created_run_config_id = ( + reloaded_job.created_run_config_id + ) + prompt_optimization_job.optimized_prompt = reloaded_job.optimized_prompt return - result_response = ( - await get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio( - job_id=gepa_job.job_id, - client=server_client, - ) + result_response = await get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio( + job_id=prompt_optimization_job.job_id, + client=server_client, ) if ( @@ -241,84 +252,98 @@ async def _create_artifacts_for_succeeded_job( and hasattr(result_response.output, "optimized_prompt") ): optimized_prompt_text = result_response.output.optimized_prompt - gepa_job.optimized_prompt = optimized_prompt_text + prompt_optimization_job.optimized_prompt = optimized_prompt_text prompt: Prompt | None = None run_config: TaskRunConfig | None = None try: prompt = create_prompt_from_optimization( - gepa_job, task, optimized_prompt_text + prompt_optimization_job, task, optimized_prompt_text ) - gepa_job.created_prompt_id = f"id::{prompt.id}" + prompt_optimization_job.created_prompt_id = f"id::{prompt.id}" - run_config = create_run_config_from_optimization(gepa_job, task, prompt) - gepa_job.created_run_config_id = run_config.id + run_config = create_run_config_from_optimization( + prompt_optimization_job, task, prompt + ) + prompt_optimization_job.created_run_config_id = run_config.id except Exception as e: logger.error( - f"Failed to create artifacts for GEPA job {gepa_job.job_id}: {e}. " + f"Failed to create artifacts for Prompt Optimization job {prompt_optimization_job.job_id}: {e}. " f"Cleaning up any created artifacts to allow retry on next invocation.", exc_info=True, ) - _cleanup_artifact(prompt, "prompt", gepa_job.job_id) - _cleanup_artifact(run_config, "run config", gepa_job.job_id) - gepa_job.created_prompt_id = None - gepa_job.created_run_config_id = None + _cleanup_artifact(prompt, "prompt", prompt_optimization_job.job_id) + _cleanup_artifact(run_config, "run config", prompt_optimization_job.job_id) + prompt_optimization_job.created_prompt_id = None + prompt_optimization_job.created_run_config_id = None -async def update_gepa_job_and_create_artifacts( - gepa_job: GepaJob, server_client: AuthenticatedClient -) -> GepaJob: +async def update_prompt_optimization_job_and_create_artifacts( + prompt_optimization_job: PromptOptimizationJob, server_client: AuthenticatedClient +) -> PromptOptimizationJob: """ - Update the status of a GepaJob from the remote server. + Update the status of a PromptOptimizationJob from the remote server. If the job has succeeded for the first time, create a prompt and run config from the result. Uses per-job locking to ensure the success transition is handled atomically. """ - task = gepa_job.parent_task() + task = prompt_optimization_job.parent_task() if task is None: - raise HTTPException(status_code=500, detail="GepaJob has no parent task") + raise HTTPException( + status_code=500, detail="PromptOptimizationJob has no parent task" + ) try: status_response = ( await get_job_status_v1_jobs_job_type_job_id_status_get.asyncio( job_type=JobType.GEPA_JOB, - job_id=gepa_job.job_id, + job_id=prompt_optimization_job.job_id, client=server_client, ) ) if status_response is None or isinstance(status_response, HTTPValidationError): - logger.warning(f"Could not fetch status for GEPA job {gepa_job.job_id}") - return gepa_job + logger.warning( + f"Could not fetch status for Prompt Optimization job {prompt_optimization_job.job_id}" + ) + raise RuntimeError( + f"Could not fetch status for Prompt Optimization job {prompt_optimization_job.job_id}: {status_response}" + ) new_status = str(status_response.status.value) - async with shared_async_lock_manager.acquire(gepa_job.job_id): - previous_status = gepa_job.latest_status - gepa_job.latest_status = new_status + async with shared_async_lock_manager.acquire(prompt_optimization_job.job_id): + previous_status = prompt_optimization_job.latest_status + prompt_optimization_job.latest_status = new_status if ( previous_status != JobStatus.SUCCEEDED and new_status == JobStatus.SUCCEEDED ): - await _create_artifacts_for_succeeded_job(gepa_job, task, server_client) + await _create_artifacts_for_succeeded_job( + prompt_optimization_job, task, server_client + ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() except Exception as e: - logger.error(f"Error updating GEPA job status: {e}", exc_info=True) + logger.error( + f"Error updating Prompt Optimization job status: {e}", exc_info=True + ) - return gepa_job + return prompt_optimization_job -def connect_gepa_job_api(app: FastAPI): - @app.get("/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_run_config") +def connect_prompt_optimization_job_api(app: FastAPI): + @app.get( + "/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_run_config" + ) async def check_run_config( project_id: str, task_id: str, run_config_id: str ) -> CheckRunConfigResponse: """ - Check if a run config is valid for a GEPA job by validating the model is supported. + Check if a run config is valid for a Prompt Optimization job by validating the model is supported. """ try: run_config = task_run_config_from_id(project_id, task_id, run_config_id) @@ -345,7 +370,7 @@ async def check_run_config( status_code=500, detail="Server client not authenticated" ) - response = await check_model_supported_v1_jobs_gepa_job_check_model_supported_get.asyncio( + response = await check_prompt_optimization_model_supported_v1_jobs_prompt_optimization_job_check_model_supported_get.asyncio( client=server_client, model_name=model_name, model_provider_name=model_provider.value, @@ -375,12 +400,14 @@ async def check_run_config( status_code=500, detail=f"Failed to check run config: {str(e)}" ) - @app.get("/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_eval") + @app.get( + "/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_eval" + ) async def check_eval( project_id: str, task_id: str, eval_id: str ) -> CheckEvalResponse: """ - Check if an eval is valid for a GEPA job. + Check if an eval is valid for a Prompt Optimization job. Validates that the eval has a default config and that the model is supported. """ try: @@ -424,7 +451,7 @@ async def check_eval( ) # EvalConfig.model_provider is already a string, no need for .value - response = await check_model_supported_v1_jobs_gepa_job_check_model_supported_get.asyncio( + response = await check_prompt_optimization_model_supported_v1_jobs_prompt_optimization_job_check_model_supported_get.asyncio( client=server_client, model_name=model_name, model_provider_name=model_provider, @@ -458,15 +485,17 @@ async def check_eval( status_code=500, detail=f"Failed to check eval: {str(e)}" ) - @app.post("/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/start") - async def start_gepa_job( + @app.post( + "/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/start" + ) + async def start_prompt_optimization_job( project_id: str, task_id: str, - request: StartGepaJobRequest, - ) -> GepaJob: + request: StartPromptOptimizationJobRequest, + ) -> PromptOptimizationJob: """ - Start a GEPA job by zipping the project and sending it to the Kiln server. - Creates and saves a GepaJob datamodel to track the job. + Start a prompt optimization job by zipping the project and sending it to the Kiln server. + Creates and saves a PromptOptimizationJob datamodel to track the job. """ task = task_from_id(project_id, task_id) project = task.parent_project() @@ -485,7 +514,7 @@ async def start_gepa_job( ): raise HTTPException( status_code=400, - detail="GEPA does not support run configurations with tools", + detail="Prompt Optimization does not support run configurations with tools", ) server_client = get_authenticated_client(_get_api_key()) if not isinstance(server_client, AuthenticatedClient): @@ -493,8 +522,10 @@ async def start_gepa_job( status_code=500, detail="Server client not authenticated" ) - with tempfile.TemporaryDirectory(prefix="kiln_gepa_") as tmpdir: - tmp_file = Path(tmpdir) / "kiln_gepa_project.zip" + with tempfile.TemporaryDirectory( + prefix="kiln_prompt_optimization_" + ) as tmpdir: + tmp_file = Path(tmpdir) / "kiln_prompt_optimization_project.zip" package_project_for_training( project=project, task_ids=[task_id], @@ -519,31 +550,29 @@ async def start_gepa_job( ) # Create the request body - body = BodyStartGepaJobV1JobsGepaJobStartPost( + body = BodyStartPromptOptimizationJobV1JobsPromptOptimizationJobStartPost( task_id=task_id, target_run_config_id=request.target_run_config_id, project_zip=project_zip_file, eval_ids=request.eval_ids, ) - detailed_response = ( - await start_gepa_job_v1_jobs_gepa_job_start_post.asyncio_detailed( - client=server_client, body=body - ) + detailed_response = await start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.asyncio_detailed( + client=server_client, body=body ) check_response_error( detailed_response, - default_detail="Failed to start GEPA job: unexpected error from server", + default_detail="Failed to start Prompt Optimization job: unexpected error from server", ) response = detailed_response.parsed if response is None or isinstance(response, HTTPValidationError): raise HTTPException( status_code=500, - detail="Failed to start GEPA job: unexpected response from server", + detail="Failed to start Prompt Optimization job: unexpected response from server", ) - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name=generate_memorable_name(), job_id=response.job_id, target_run_config_id=request.target_run_config_id, @@ -551,14 +580,14 @@ async def start_gepa_job( eval_ids=request.eval_ids, parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() - return gepa_job + return prompt_optimization_job except HTTPException: raise except Exception as e: - logger.error(f"Error starting GEPA job: {e}", exc_info=True) + logger.error(f"Error starting Prompt Optimization job: {e}", exc_info=True) # Provide more specific error messages if "ReadError" in str(type(e).__name__) or "timeout" in str(e).lower(): @@ -568,19 +597,20 @@ async def start_gepa_job( ) raise HTTPException( - status_code=500, detail=f"Failed to start GEPA job: {str(e)}" + status_code=500, + detail=f"Failed to start Prompt Optimization job: {str(e)}", ) - @app.get("/api/projects/{project_id}/tasks/{task_id}/gepa_jobs") - async def list_gepa_jobs( + @app.get("/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs") + async def list_prompt_optimization_jobs( project_id: str, task_id: str, update_status: bool = False - ) -> list[GepaJob]: + ) -> list[PromptOptimizationJob]: """ - List all GEPA jobs for a task. + List all Prompt Optimization jobs for a task. Optionally update the status of non-final jobs from the remote server. """ task = task_from_id(project_id, task_id) - gepa_jobs = task.gepa_jobs() + prompt_optimization_jobs = task.prompt_optimization_jobs() if update_status: try: @@ -589,7 +619,7 @@ async def list_gepa_jobs( # Filter jobs that need status updates jobs_to_update = [ job - for job in gepa_jobs + for job in prompt_optimization_jobs if not is_job_status_final(job.latest_status) ] @@ -597,46 +627,65 @@ async def list_gepa_jobs( batch_size = 5 for i in range(0, len(jobs_to_update), batch_size): batch = jobs_to_update[i : i + batch_size] + + # this swallows the exceptions from each call, which is fine await asyncio.gather( *[ - update_gepa_job_and_create_artifacts(job, server_client) + update_prompt_optimization_job_and_create_artifacts( + job, server_client + ) for job in batch ] ) except Exception as e: - logger.error(f"Error updating GEPA job statuses: {e}", exc_info=True) + logger.error( + f"Error updating Prompt Optimization job statuses: {e}", + exc_info=True, + ) - return gepa_jobs + return prompt_optimization_jobs - @app.get("/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}") - async def get_gepa_job(project_id: str, task_id: str, gepa_job_id: str) -> GepaJob: + @app.get( + "/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" + ) + async def get_prompt_optimization_job( + project_id: str, task_id: str, prompt_optimization_job_id: str + ) -> PromptOptimizationJob: """ - Get a specific GEPA job and update its status from the remote server. + Get a specific Prompt Optimization job and update its status from the remote server. If the job has succeeded, create a prompt if one doesn't exist yet. If the job is already in a settled state (succeeded, failed, cancelled), skip the status update and return the cached model. """ - gepa_job = gepa_job_from_id(project_id, task_id, gepa_job_id) + prompt_optimization_job = prompt_optimization_job_from_id( + project_id, task_id, prompt_optimization_job_id + ) # Skip status update if job is already in a final state - if is_job_status_final(gepa_job.latest_status): - return gepa_job + if is_job_status_final(prompt_optimization_job.latest_status): + return prompt_optimization_job try: server_client = get_authenticated_client(_get_api_key()) if isinstance(server_client, AuthenticatedClient): - gepa_job = await update_gepa_job_and_create_artifacts( - gepa_job, server_client + prompt_optimization_job = ( + await update_prompt_optimization_job_and_create_artifacts( + prompt_optimization_job, server_client + ) ) except Exception as e: - logger.error(f"Error updating GEPA job status: {e}", exc_info=True) + logger.error( + f"Error updating Prompt Optimization job status: {e}", exc_info=True + ) - return gepa_job + return prompt_optimization_job - @app.get("/api/gepa_jobs/{job_id}/status") - async def get_gepa_job_status(job_id: str) -> PublicGEPAJobStatusResponse: + @app.get("/api/prompt_optimization_jobs/{job_id}/status") + async def get_prompt_optimization_job_status( + job_id: str, + ) -> PublicPromptOptimizationJobStatusResponse: """ - Get the status of a GEPA job. + Get the status of a Prompt Optimization job. """ try: server_client = get_authenticated_client(_get_api_key()) @@ -653,26 +702,31 @@ async def get_gepa_job_status(job_id: str) -> PublicGEPAJobStatusResponse: if response is None or isinstance(response, HTTPValidationError): raise HTTPException( - status_code=404, detail=f"GEPA job {job_id} not found" + status_code=404, + detail=f"Prompt Optimization job {job_id} not found", ) - return PublicGEPAJobStatusResponse( + return PublicPromptOptimizationJobStatusResponse( job_id=response.job_id, status=response.status ) except HTTPException: raise except Exception as e: - logger.error(f"Error getting GEPA job status: {e}", exc_info=True) + logger.error( + f"Error getting prompt optimization job status: {e}", exc_info=True + ) raise HTTPException( status_code=500, - detail=f"Failed to get GEPA job status: {str(e)}", + detail=f"Failed to get Prompt Optimization job status: {str(e)}", ) - @app.get("/api/gepa_jobs/{job_id}/result") - async def get_gepa_job_result(job_id: str) -> PublicGEPAJobResultResponse: + @app.get("/api/prompt_optimization_jobs/{job_id}/result") + async def get_prompt_optimization_job_result( + job_id: str, + ) -> PublicPromptOptimizationJobResultResponse: """ - Get the result of a GEPA job (includes status and output if completed). + Get the result of a prompt optimization job (includes status and output if completed). """ try: server_client = get_authenticated_client(_get_api_key()) @@ -681,33 +735,34 @@ async def get_gepa_job_result(job_id: str) -> PublicGEPAJobResultResponse: status_code=500, detail="Server client not authenticated" ) - response = ( - await get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio( - job_id=job_id, - client=server_client, - ) + response = await get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio( + job_id=job_id, + client=server_client, ) if response is None or isinstance(response, HTTPValidationError): raise HTTPException( - status_code=404, detail=f"GEPA job {job_id} result not found" + status_code=404, + detail=f"Prompt Optimization job {job_id} result not found", ) if not response.output or not hasattr(response.output, "optimized_prompt"): raise HTTPException( status_code=500, - detail=f"GEPA job {job_id} completed but has no output", + detail=f"Prompt Optimization job {job_id} completed but has no output", ) - return PublicGEPAJobResultResponse( + return PublicPromptOptimizationJobResultResponse( optimized_prompt=response.output.optimized_prompt ) except HTTPException: raise except Exception as e: - logger.error(f"Error getting GEPA job result: {e}", exc_info=True) + logger.error( + f"Error getting prompt optimization job result: {e}", exc_info=True + ) raise HTTPException( status_code=500, - detail=f"Failed to get GEPA job result: {str(e)}", + detail=f"Failed to get Prompt Optimization job result: {str(e)}", ) diff --git a/app/desktop/studio_server/test_gepa_job_api.py b/app/desktop/studio_server/test_prompt_optimization_job_api.py similarity index 68% rename from app/desktop/studio_server/test_gepa_job_api.py rename to app/desktop/studio_server/test_prompt_optimization_job_api.py index cac2bf35c..b0a935fd5 100644 --- a/app/desktop/studio_server/test_gepa_job_api.py +++ b/app/desktop/studio_server/test_prompt_optimization_job_api.py @@ -8,12 +8,6 @@ from app.desktop.studio_server.api_client.kiln_ai_server_client.client import ( AuthenticatedClient, ) -from app.desktop.studio_server.api_client.kiln_ai_server_client.models.gepa_job_output import ( - GEPAJobOutput, -) -from app.desktop.studio_server.api_client.kiln_ai_server_client.models.gepa_job_result_response import ( - GEPAJobResultResponse, -) from app.desktop.studio_server.api_client.kiln_ai_server_client.models.http_validation_error import ( HTTPValidationError, ) @@ -26,21 +20,27 @@ from app.desktop.studio_server.api_client.kiln_ai_server_client.models.job_status_response import ( JobStatusResponse, ) +from app.desktop.studio_server.api_client.kiln_ai_server_client.models.prompt_optimization_job_output import ( + PromptOptimizationJobOutput, +) +from app.desktop.studio_server.api_client.kiln_ai_server_client.models.prompt_optimization_job_result_response import ( + PromptOptimizationJobResultResponse, +) from app.desktop.studio_server.api_client.kiln_ai_server_client.types import ( Response as SdkResponse, ) -from app.desktop.studio_server.gepa_job_api import ( - PublicGEPAJobResultResponse, - PublicGEPAJobStatusResponse, - connect_gepa_job_api, - gepa_job_from_id, +from app.desktop.studio_server.prompt_optimization_job_api import ( + PublicPromptOptimizationJobResultResponse, + PublicPromptOptimizationJobStatusResponse, + connect_prompt_optimization_job_api, is_job_status_final, - update_gepa_job_and_create_artifacts, + prompt_optimization_job_from_id, + update_prompt_optimization_job_and_create_artifacts, ) from fastapi import FastAPI, HTTPException from fastapi.testclient import TestClient from kiln_ai.cli.commands.package_project import PackageForTrainingConfig -from kiln_ai.datamodel import GepaJob, Project, Task +from kiln_ai.datamodel import Project, PromptOptimizationJob, Task from kiln_ai.datamodel.datamodel_enums import ModelProviderName, StructuredOutputMode from kiln_ai.datamodel.run_config import RunConfigProperties from kiln_ai.datamodel.task import TaskRunConfig @@ -71,7 +71,7 @@ def _make_sdk_response( @pytest.fixture def app(): app = FastAPI() - connect_gepa_job_api(app) + connect_prompt_optimization_job_api(app) return app @@ -83,7 +83,9 @@ def client(app): @pytest.fixture def mock_api_key(): with ( - patch("app.desktop.studio_server.gepa_job_api.Config.shared") as mock_config, + patch( + "app.desktop.studio_server.prompt_optimization_job_api.Config.shared" + ) as mock_config, patch("kiln_ai.datamodel.basemodel.Config.shared") as mock_basemodel_config, ): mock_config_instance = mock_config.return_value @@ -96,79 +98,86 @@ def mock_api_key(): yield mock_config_instance -def test_get_gepa_job_result_success(client, mock_api_key): - """Test successfully getting a GEPA job result.""" +def test_get_prompt_optimization_job_result_success(client, mock_api_key): + """Test successfully getting a Prompt Optimization job result.""" job_id = "test-job-123" expected_prompt = "This is the optimized prompt" - mock_output = GEPAJobOutput(optimized_prompt=expected_prompt) - mock_response = GEPAJobResultResponse( + mock_output = PromptOptimizationJobOutput(optimized_prompt=expected_prompt) + mock_response = PromptOptimizationJobResultResponse( status=JobStatus.SUCCEEDED, output=mock_output ) with patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_response, ): - response = client.get(f"/api/gepa_jobs/{job_id}/result") + response = client.get(f"/api/prompt_optimization_jobs/{job_id}/result") assert response.status_code == 200 assert response.json() == {"optimized_prompt": expected_prompt} - result = PublicGEPAJobResultResponse(**response.json()) + result = PublicPromptOptimizationJobResultResponse(**response.json()) assert result.optimized_prompt == expected_prompt -def test_get_gepa_job_result_not_found(client, mock_api_key): - """Test getting a GEPA job result that doesn't exist.""" +def test_get_prompt_optimization_job_result_not_found(client, mock_api_key): + """Test getting a Prompt Optimization job result that doesn't exist.""" job_id = "nonexistent-job" with patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=None, ): - response = client.get(f"/api/gepa_jobs/{job_id}/result") + response = client.get(f"/api/prompt_optimization_jobs/{job_id}/result") assert response.status_code == 404 - assert f"GEPA job {job_id} result not found" in response.json()["detail"] + assert ( + f"Prompt Optimization job {job_id} result not found" + in response.json()["detail"] + ) -def test_get_gepa_job_result_no_output(client, mock_api_key): - """Test getting a GEPA job result that has no output.""" +def test_get_prompt_optimization_job_result_no_output(client, mock_api_key): + """Test getting a Prompt Optimization job result that has no output.""" job_id = "test-job-no-output" - mock_response = GEPAJobResultResponse(status=JobStatus.RUNNING, output=None) + mock_response = PromptOptimizationJobResultResponse( + status=JobStatus.RUNNING, output=None + ) with patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_response, ): - response = client.get(f"/api/gepa_jobs/{job_id}/result") + response = client.get(f"/api/prompt_optimization_jobs/{job_id}/result") assert response.status_code == 500 assert "has no output" in response.json()["detail"] -def test_get_gepa_job_result_api_error(client, mock_api_key): - """Test handling of API errors when getting GEPA job result.""" +def test_get_prompt_optimization_job_result_api_error(client, mock_api_key): + """Test handling of API errors when getting Prompt Optimization job result.""" job_id = "test-job-error" with patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, side_effect=Exception("API connection failed"), ): - response = client.get(f"/api/gepa_jobs/{job_id}/result") + response = client.get(f"/api/prompt_optimization_jobs/{job_id}/result") assert response.status_code == 500 - assert "Failed to get GEPA job result" in response.json()["detail"] + assert ( + "Failed to get Prompt Optimization job result" in response.json()["detail"] + ) -def test_get_gepa_job_status_success(client, mock_api_key): - """Test successfully getting GEPA job status.""" +def test_get_prompt_optimization_job_status_success(client, mock_api_key): + """Test successfully getting Prompt Optimization job status.""" job_id = "test-job-123" mock_response = JobStatusResponse(job_id=job_id, status=JobStatus.RUNNING) @@ -178,17 +187,17 @@ def test_get_gepa_job_status_success(client, mock_api_key): new_callable=AsyncMock, return_value=mock_response, ): - response = client.get(f"/api/gepa_jobs/{job_id}/status") + response = client.get(f"/api/prompt_optimization_jobs/{job_id}/status") assert response.status_code == 200 assert response.json() == {"job_id": job_id, "status": "running"} - result = PublicGEPAJobStatusResponse(**response.json()) + result = PublicPromptOptimizationJobStatusResponse(**response.json()) assert result.job_id == job_id assert result.status == JobStatus.RUNNING -def test_get_gepa_job_status_not_found(client, mock_api_key): +def test_get_prompt_optimization_job_status_not_found(client, mock_api_key): """Test getting status for a job that doesn't exist.""" job_id = "nonexistent-job" @@ -197,46 +206,50 @@ def test_get_gepa_job_status_not_found(client, mock_api_key): new_callable=AsyncMock, return_value=None, ): - response = client.get(f"/api/gepa_jobs/{job_id}/status") + response = client.get(f"/api/prompt_optimization_jobs/{job_id}/status") assert response.status_code == 404 - assert f"GEPA job {job_id} not found" in response.json()["detail"] + assert ( + f"Prompt Optimization job {job_id} not found" in response.json()["detail"] + ) -def test_get_gepa_job_result_no_api_key(client): - """Test getting GEPA job result without API key configured.""" +def test_get_prompt_optimization_job_result_no_api_key(client): + """Test getting Prompt Optimization job result without API key configured.""" job_id = "test-job-123" - with patch("app.desktop.studio_server.gepa_job_api.Config.shared") as mock_config: + with patch( + "app.desktop.studio_server.prompt_optimization_job_api.Config.shared" + ) as mock_config: mock_config_instance = mock_config.return_value mock_config_instance.kiln_copilot_api_key = None - response = client.get(f"/api/gepa_jobs/{job_id}/result") + response = client.get(f"/api/prompt_optimization_jobs/{job_id}/result") assert response.status_code == 401 assert "API key not configured" in response.json()["detail"] -def test_get_gepa_job_result_validation_error(client, mock_api_key): - """Test getting GEPA job result with validation error from server.""" +def test_get_prompt_optimization_job_result_validation_error(client, mock_api_key): + """Test getting Prompt Optimization job result with validation error from server.""" job_id = "test-job-123" mock_error = HTTPValidationError(detail=[]) with patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_error, ): - response = client.get(f"/api/gepa_jobs/{job_id}/result") + response = client.get(f"/api/prompt_optimization_jobs/{job_id}/result") assert response.status_code == 404 -def test_public_gepa_job_result_response_model(): - """Test the PublicGEPAJobResultResponse Pydantic model.""" +def test_public_prompt_optimization_job_result_response_model(): + """Test the PublicPromptOptimizationJobResultResponse Pydantic model.""" prompt = "Test optimized prompt" - model = PublicGEPAJobResultResponse(optimized_prompt=prompt) + model = PublicPromptOptimizationJobResultResponse(optimized_prompt=prompt) assert model.optimized_prompt == prompt assert model.model_dump() == {"optimized_prompt": prompt} @@ -244,15 +257,15 @@ def test_public_gepa_job_result_response_model(): json_str = model.model_dump_json() assert prompt in json_str - parsed = PublicGEPAJobResultResponse.model_validate_json(json_str) + parsed = PublicPromptOptimizationJobResultResponse.model_validate_json(json_str) assert parsed.optimized_prompt == prompt -def test_public_gepa_job_status_response_model(): - """Test the PublicGEPAJobStatusResponse Pydantic model.""" +def test_public_prompt_optimization_job_status_response_model(): + """Test the PublicPromptOptimizationJobStatusResponse Pydantic model.""" job_id = "test-job-123" status = JobStatus.RUNNING - model = PublicGEPAJobStatusResponse(job_id=job_id, status=status) + model = PublicPromptOptimizationJobStatusResponse(job_id=job_id, status=status) assert model.job_id == job_id assert model.status == JobStatus.RUNNING @@ -262,19 +275,21 @@ def test_public_gepa_job_status_response_model(): assert job_id in json_str assert "running" in json_str - parsed = PublicGEPAJobStatusResponse.model_validate_json(json_str) + parsed = PublicPromptOptimizationJobStatusResponse.model_validate_json(json_str) assert parsed.job_id == job_id assert parsed.status == JobStatus.RUNNING -def test_start_gepa_job_creates_datamodel(client, mock_api_key, tmp_path): - """Test that starting a GEPA job creates and saves a GepaJob datamodel.""" +def test_start_prompt_optimization_job_creates_datamodel( + client, mock_api_key, tmp_path +): + """Test that starting a Prompt Optimization job creates and saves a PromptOptimizationJob datamodel.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) @@ -288,19 +303,19 @@ def test_start_gepa_job_creates_datamodel(client, mock_api_key, tmp_path): with ( patch( - "app.desktop.studio_server.gepa_job_api.task_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", return_value=task, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.package_project_for_training", + "app.desktop.studio_server.prompt_optimization_job_api.package_project_for_training", side_effect=_mock_package_project_for_training, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.start_gepa_job_v1_jobs_gepa_job_start_post.asyncio_detailed", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.asyncio_detailed", new_callable=AsyncMock, return_value=_make_sdk_response( parsed=JobStartResponse(job_id="remote-job-123") @@ -308,7 +323,7 @@ def test_start_gepa_job_creates_datamodel(client, mock_api_key, tmp_path): ), ): response = client.post( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/start", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/start", json={ "target_run_config_id": "test-run-config-id", "eval_ids": ["eval-1", "eval-2"], @@ -324,22 +339,22 @@ def test_start_gepa_job_creates_datamodel(client, mock_api_key, tmp_path): assert "id" in result assert "name" in result - gepa_jobs = task.gepa_jobs() - assert len(gepa_jobs) == 1 - assert gepa_jobs[0].job_id == "remote-job-123" - assert gepa_jobs[0].eval_ids == ["eval-1", "eval-2"] + prompt_optimization_jobs = task.prompt_optimization_jobs() + assert len(prompt_optimization_jobs) == 1 + assert prompt_optimization_jobs[0].job_id == "remote-job-123" + assert prompt_optimization_jobs[0].eval_ids == ["eval-1", "eval-2"] -def test_start_gepa_job_calls_package_with_correct_params( +def test_start_prompt_optimization_job_calls_package_with_correct_params( client, mock_api_key, tmp_path ): - """Test that start_gepa_job calls package_project_for_training with the correct arguments.""" + """Test that start_prompt_optimization_job calls package_project_for_training with the correct arguments.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) @@ -355,19 +370,19 @@ def test_start_gepa_job_calls_package_with_correct_params( with ( patch( - "app.desktop.studio_server.gepa_job_api.task_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", return_value=task, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.package_project_for_training", + "app.desktop.studio_server.prompt_optimization_job_api.package_project_for_training", mock_packager, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.start_gepa_job_v1_jobs_gepa_job_start_post.asyncio_detailed", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.asyncio_detailed", new_callable=AsyncMock, return_value=_make_sdk_response( parsed=JobStartResponse(job_id="remote-job-456") @@ -375,7 +390,7 @@ def test_start_gepa_job_calls_package_with_correct_params( ), ): response = client.post( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/start", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/start", json={ "target_run_config_id": "rc-123", "eval_ids": ["eval-a", "eval-b"], @@ -397,45 +412,46 @@ def test_start_gepa_job_calls_package_with_correct_params( assert call_kwargs["config"].exclude_eval_config_runs is True -def test_list_gepa_jobs(client, mock_api_key, tmp_path): - """Test listing all GEPA jobs for a task.""" +def test_list_prompt_optimization_jobs(client, mock_api_key, tmp_path): + """Test listing all Prompt Optimization jobs for a task.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() - gepa_job_1 = GepaJob( + prompt_optimization_job_1 = PromptOptimizationJob( name="Job 1", job_id="remote-job-1", target_run_config_id="config-1", latest_status="pending", parent=task, ) - gepa_job_1.save_to_file() + prompt_optimization_job_1.save_to_file() - gepa_job_2 = GepaJob( + prompt_optimization_job_2 = PromptOptimizationJob( name="Job 2", job_id="remote-job-2", target_run_config_id="config-2", latest_status="succeeded", parent=task, ) - gepa_job_2.save_to_file() + prompt_optimization_job_2.save_to_file() project_id = project.id task_id = task.id with patch( - "app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs", params={"update_status": False}, ) @@ -448,38 +464,41 @@ def test_list_gepa_jobs(client, mock_api_key, tmp_path): assert job_ids == {"remote-job-1", "remote-job-2"} -def test_get_gepa_job_detail(client, mock_api_key, tmp_path): - """Test getting GEPA job detail.""" +def test_get_prompt_optimization_job_detail(client, mock_api_key, tmp_path): + """Test getting Prompt Optimization job detail.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id="config-1", latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id - gepa_job_id = gepa_job.id + prompt_optimization_job_id = prompt_optimization_job.id mock_status_response = JobStatusResponse( job_id="remote-job-123", status=JobStatus.RUNNING ) with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), patch( "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_job_status_v1_jobs_job_type_job_id_status_get.asyncio", new_callable=AsyncMock, @@ -487,7 +506,7 @@ def test_get_gepa_job_detail(client, mock_api_key, tmp_path): ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response.status_code == 200 @@ -497,14 +516,16 @@ def test_get_gepa_job_detail(client, mock_api_key, tmp_path): assert result["latest_status"] == "running" -def test_gepa_job_creates_prompt_on_success(client, mock_api_key, tmp_path): - """Test that a prompt is created when a GEPA job succeeds.""" +def test_prompt_optimization_job_creates_prompt_on_success( + client, mock_api_key, tmp_path +): + """Test that a prompt is created when a Prompt Optimization job succeeds.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) @@ -522,47 +543,50 @@ def test_gepa_job_creates_prompt_on_success(client, mock_api_key, tmp_path): ) target_run_config.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id=target_run_config.id, latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id - gepa_job_id = gepa_job.id + prompt_optimization_job_id = prompt_optimization_job.id - optimized_prompt = "This is the optimized prompt from GEPA" + optimized_prompt = "This is the optimized prompt from Prompt Optimization" mock_status_response = JobStatusResponse( job_id="remote-job-123", status=JobStatus.SUCCEEDED ) - mock_result_response = GEPAJobResultResponse( + mock_result_response = PromptOptimizationJobResultResponse( status=JobStatus.SUCCEEDED, - output=GEPAJobOutput(optimized_prompt=optimized_prompt), + output=PromptOptimizationJobOutput(optimized_prompt=optimized_prompt), ) with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), patch( "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_job_status_v1_jobs_job_type_job_id_status_get.asyncio", new_callable=AsyncMock, return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_result_response, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=target_run_config, ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response.status_code == 200 @@ -573,18 +597,20 @@ def test_gepa_job_creates_prompt_on_success(client, mock_api_key, tmp_path): prompts = task.prompts() assert len(prompts) == 1 assert prompts[0].prompt == optimized_prompt - assert prompts[0].name == gepa_job.name + assert prompts[0].name == prompt_optimization_job.name assert result["created_prompt_id"] == f"id::{prompts[0].id}" -def test_gepa_job_only_creates_prompt_once(client, mock_api_key, tmp_path): +def test_prompt_optimization_job_only_creates_prompt_once( + client, mock_api_key, tmp_path +): """Test that a prompt is only created once even if status is checked multiple times.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) @@ -602,52 +628,55 @@ def test_gepa_job_only_creates_prompt_once(client, mock_api_key, tmp_path): ) target_run_config.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id=target_run_config.id, latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id - gepa_job_id = gepa_job.id + prompt_optimization_job_id = prompt_optimization_job.id - optimized_prompt = "This is the optimized prompt from GEPA" + optimized_prompt = "This is the optimized prompt from Prompt Optimization" mock_status_response = JobStatusResponse( job_id="remote-job-123", status=JobStatus.SUCCEEDED ) - mock_result_response = GEPAJobResultResponse( + mock_result_response = PromptOptimizationJobResultResponse( status=JobStatus.SUCCEEDED, - output=GEPAJobOutput(optimized_prompt=optimized_prompt), + output=PromptOptimizationJobOutput(optimized_prompt=optimized_prompt), ) with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), patch( "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_job_status_v1_jobs_job_type_job_id_status_get.asyncio", new_callable=AsyncMock, return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_result_response, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=target_run_config, ), ): response_1 = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response_1.status_code == 200 response_2 = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response_2.status_code == 200 @@ -655,20 +684,22 @@ def test_gepa_job_only_creates_prompt_once(client, mock_api_key, tmp_path): assert len(prompts) == 1 -def test_get_gepa_job_skips_update_when_succeeded(client, mock_api_key, tmp_path): +def test_get_prompt_optimization_job_skips_update_when_succeeded( + client, mock_api_key, tmp_path +): """Test that getting a job that's already succeeded skips the API status update.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id="config-1", @@ -676,25 +707,28 @@ def test_get_gepa_job_skips_update_when_succeeded(client, mock_api_key, tmp_path optimized_prompt="Already optimized", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id - gepa_job_id = gepa_job.id + prompt_optimization_job_id = prompt_optimization_job.id with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), patch( "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_job_status_v1_jobs_job_type_job_id_status_get.asyncio", new_callable=AsyncMock, ) as mock_status, patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, ) as mock_result, ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response.status_code == 200 @@ -706,45 +740,50 @@ def test_get_gepa_job_skips_update_when_succeeded(client, mock_api_key, tmp_path mock_result.assert_not_called() -def test_get_gepa_job_skips_update_when_failed(client, mock_api_key, tmp_path): +def test_get_prompt_optimization_job_skips_update_when_failed( + client, mock_api_key, tmp_path +): """Test that getting a job that's already failed skips the API status update.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id="config-1", latest_status="failed", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id - gepa_job_id = gepa_job.id + prompt_optimization_job_id = prompt_optimization_job.id with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), patch( "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_job_status_v1_jobs_job_type_job_id_status_get.asyncio", new_callable=AsyncMock, ) as mock_status, patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, ) as mock_result, ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response.status_code == 200 @@ -755,45 +794,50 @@ def test_get_gepa_job_skips_update_when_failed(client, mock_api_key, tmp_path): mock_result.assert_not_called() -def test_get_gepa_job_skips_update_when_cancelled(client, mock_api_key, tmp_path): +def test_get_prompt_optimization_job_skips_update_when_cancelled( + client, mock_api_key, tmp_path +): """Test that getting a job that's already cancelled skips the API status update.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id="config-1", latest_status="cancelled", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id - gepa_job_id = gepa_job.id + prompt_optimization_job_id = prompt_optimization_job.id with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), patch( "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_job_status_v1_jobs_job_type_job_id_status_get.asyncio", new_callable=AsyncMock, ) as mock_status, patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, ) as mock_result, ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response.status_code == 200 @@ -820,16 +864,16 @@ def test_is_job_status_final(): assert is_job_status_final("running") is False -def test_list_gepa_jobs_updates_statuses_in_parallel_batches( +def test_list_prompt_optimization_jobs_updates_statuses_in_parallel_batches( client, mock_api_key, tmp_path ): - """Test that list_gepa_jobs updates statuses in parallel batches of 5.""" + """Test that list_prompt_optimization_jobs updates statuses in parallel batches of 5.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) @@ -837,7 +881,7 @@ def test_list_gepa_jobs_updates_statuses_in_parallel_batches( # Create 12 jobs with non-final statuses and 3 with final statuses for i in range(12): - GepaJob( + PromptOptimizationJob( name=f"Job {i}", job_id=f"remote-job-{i}", target_run_config_id="config-1", @@ -848,7 +892,7 @@ def test_list_gepa_jobs_updates_statuses_in_parallel_batches( ).save_to_file() for i in range(12, 15): - GepaJob( + PromptOptimizationJob( name=f"Job {i}", job_id=f"remote-job-{i}", target_run_config_id="config-1", @@ -862,20 +906,23 @@ def test_list_gepa_jobs_updates_statuses_in_parallel_batches( # Track calls to the update function update_calls = [] - async def mock_update(gepa_job, client): - update_calls.append(gepa_job.job_id) - return gepa_job + async def mock_update(prompt_optimization_job, client): + update_calls.append(prompt_optimization_job.job_id) + return prompt_optimization_job with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), patch( - "app.desktop.studio_server.gepa_job_api.update_gepa_job_and_create_artifacts", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.update_prompt_optimization_job_and_create_artifacts", new_callable=AsyncMock, side_effect=mock_update, ) as mock_update_fn, ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs", params={"update_status": True}, ) @@ -893,21 +940,23 @@ async def mock_update(gepa_job, client): assert job_id not in succeeded_job_ids -def test_list_gepa_jobs_skips_final_status_updates(client, mock_api_key, tmp_path): - """Test that list_gepa_jobs skips updating jobs with final statuses.""" +def test_list_prompt_optimization_jobs_skips_final_status_updates( + client, mock_api_key, tmp_path +): + """Test that list_prompt_optimization_jobs skips updating jobs with final statuses.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() # Create jobs with final statuses - GepaJob( + PromptOptimizationJob( name="Succeeded Job", job_id="remote-job-succeeded", target_run_config_id="config-1", @@ -915,7 +964,7 @@ def test_list_gepa_jobs_skips_final_status_updates(client, mock_api_key, tmp_pat parent=task, ).save_to_file() - GepaJob( + PromptOptimizationJob( name="Failed Job", job_id="remote-job-failed", target_run_config_id="config-1", @@ -923,7 +972,7 @@ def test_list_gepa_jobs_skips_final_status_updates(client, mock_api_key, tmp_pat parent=task, ).save_to_file() - GepaJob( + PromptOptimizationJob( name="Cancelled Job", job_id="remote-job-cancelled", target_run_config_id="config-1", @@ -935,14 +984,17 @@ def test_list_gepa_jobs_skips_final_status_updates(client, mock_api_key, tmp_pat task_id = task.id with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), patch( - "app.desktop.studio_server.gepa_job_api.update_gepa_job_and_create_artifacts", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.update_prompt_optimization_job_and_create_artifacts", new_callable=AsyncMock, ) as mock_update, ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs", params={"update_status": True}, ) @@ -954,15 +1006,15 @@ def test_list_gepa_jobs_skips_final_status_updates(client, mock_api_key, tmp_pat mock_update.assert_not_called() -def test_gepa_job_from_id_not_found(client, tmp_path): - """Test that gepa_job_from_id raises HTTPException when job not found.""" +def test_prompt_optimization_job_from_id_not_found(client, tmp_path): + """Test that prompt_optimization_job_from_id raises HTTPException when job not found.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) @@ -973,19 +1025,22 @@ def test_gepa_job_from_id_not_found(client, tmp_path): nonexistent_job_id = "nonexistent-job-id" with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), pytest.raises(Exception) as exc_info, ): - gepa_job_from_id(project_id, task_id, nonexistent_job_id) + prompt_optimization_job_from_id(project_id, task_id, nonexistent_job_id) assert exc_info.value.status_code == 404 assert "not found" in str(exc_info.value.detail) -def test_update_gepa_job_status_no_parent_task(mock_api_key): - """Test update_gepa_job_and_create_artifacts when job has no parent task.""" +def test_update_prompt_optimization_job_status_no_parent_task(mock_api_key): + """Test update_prompt_optimization_job_and_create_artifacts when job has no parent task.""" - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Orphan Job", job_id="remote-job-orphan", target_run_config_id="config-1", @@ -995,40 +1050,49 @@ def test_update_gepa_job_status_no_parent_task(mock_api_key): mock_client = MagicMock(spec=AuthenticatedClient) with pytest.raises(Exception) as exc_info: - asyncio.run(update_gepa_job_and_create_artifacts(gepa_job, mock_client)) + asyncio.run( + update_prompt_optimization_job_and_create_artifacts( + prompt_optimization_job, mock_client + ) + ) assert exc_info.value.status_code == 500 assert "no parent task" in str(exc_info.value.detail) -def test_update_gepa_job_status_response_none(client, mock_api_key, tmp_path): - """Test update_gepa_job_and_create_artifacts when status response is None.""" +def test_update_prompt_optimization_job_status_response_none( + client, mock_api_key, tmp_path +): + """Test update_prompt_optimization_job_and_create_artifacts when status response is None.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id="config-1", latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id - gepa_job_id = gepa_job.id + prompt_optimization_job_id = prompt_optimization_job.id with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), patch( "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_job_status_v1_jobs_job_type_job_id_status_get.asyncio", new_callable=AsyncMock, @@ -1036,7 +1100,7 @@ def test_update_gepa_job_status_response_none(client, mock_api_key, tmp_path): ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response.status_code == 200 @@ -1044,38 +1108,41 @@ def test_update_gepa_job_status_response_none(client, mock_api_key, tmp_path): assert result["latest_status"] == "pending" -def test_update_gepa_job_status_response_validation_error( +def test_update_prompt_optimization_job_status_response_validation_error( client, mock_api_key, tmp_path ): - """Test update_gepa_job_and_create_artifacts when status response is HTTPValidationError.""" + """Test update_prompt_optimization_job_and_create_artifacts when status response is HTTPValidationError.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id="config-1", latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id - gepa_job_id = gepa_job.id + prompt_optimization_job_id = prompt_optimization_job.id mock_error = HTTPValidationError(detail=[]) with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), patch( "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_job_status_v1_jobs_job_type_job_id_status_get.asyncio", new_callable=AsyncMock, @@ -1083,7 +1150,7 @@ def test_update_gepa_job_status_response_validation_error( ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response.status_code == 200 @@ -1091,34 +1158,39 @@ def test_update_gepa_job_status_response_validation_error( assert result["latest_status"] == "pending" -def test_update_gepa_job_status_exception_during_update(client, mock_api_key, tmp_path): - """Test that update_gepa_job_and_create_artifacts handles exceptions during status update.""" +def test_update_prompt_optimization_job_status_exception_during_update( + client, mock_api_key, tmp_path +): + """Test that update_prompt_optimization_job_and_create_artifacts handles exceptions during status update.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id="config-1", latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id - gepa_job_id = gepa_job.id + prompt_optimization_job_id = prompt_optimization_job.id with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), patch( "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_job_status_v1_jobs_job_type_job_id_status_get.asyncio", new_callable=AsyncMock, @@ -1126,7 +1198,7 @@ def test_update_gepa_job_status_exception_during_update(client, mock_api_key, tm ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response.status_code == 200 @@ -1134,41 +1206,46 @@ def test_update_gepa_job_status_exception_during_update(client, mock_api_key, tm assert result["latest_status"] == "pending" -def test_list_gepa_jobs_exception_during_update(client, mock_api_key, tmp_path): - """Test that list_gepa_jobs handles exceptions during status updates gracefully.""" +def test_list_prompt_optimization_jobs_exception_during_update( + client, mock_api_key, tmp_path +): + """Test that list_prompt_optimization_jobs handles exceptions during status updates gracefully.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id="config-1", latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), patch( - "app.desktop.studio_server.gepa_job_api.update_gepa_job_and_create_artifacts", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.update_prompt_optimization_job_and_create_artifacts", new_callable=AsyncMock, side_effect=Exception("Update failed"), ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs", params={"update_status": True}, ) @@ -1200,11 +1277,11 @@ def test_check_run_config_with_tools(client, mock_api_key, tmp_path): ] with patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_run_config", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_run_config", params={"run_config_id": run_config_id}, ) @@ -1235,11 +1312,11 @@ def test_check_run_config_missing_model_name(client, mock_api_key, tmp_path): run_config_id = "test-config-id" with patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_run_config", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_run_config", params={"run_config_id": run_config_id}, ) @@ -1270,11 +1347,11 @@ def test_check_run_config_missing_model_provider(client, mock_api_key, tmp_path) run_config_id = "test-config-id" with patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_run_config", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_run_config", params={"run_config_id": run_config_id}, ) @@ -1309,17 +1386,17 @@ def test_check_run_config_server_validation_error(client, mock_api_key, tmp_path with ( patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.check_model_supported_v1_jobs_gepa_job_check_model_supported_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.check_prompt_optimization_model_supported_v1_jobs_prompt_optimization_job_check_model_supported_get.asyncio", new_callable=AsyncMock, return_value=mock_error, ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_run_config", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_run_config", params={"run_config_id": run_config_id}, ) @@ -1350,17 +1427,17 @@ def test_check_run_config_server_none_response(client, mock_api_key, tmp_path): with ( patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.check_model_supported_v1_jobs_gepa_job_check_model_supported_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.check_prompt_optimization_model_supported_v1_jobs_prompt_optimization_job_check_model_supported_get.asyncio", new_callable=AsyncMock, return_value=None, ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_run_config", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_run_config", params={"run_config_id": run_config_id}, ) @@ -1385,11 +1462,11 @@ def test_check_run_config_exception(client, mock_api_key, tmp_path): run_config_id = "test-config-id" with patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", side_effect=Exception("Database error"), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_run_config", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_run_config", params={"run_config_id": run_config_id}, ) @@ -1418,11 +1495,11 @@ def test_check_eval_no_current_config(client, mock_api_key, tmp_path): eval_id = "test-eval-id" with patch( - "app.desktop.studio_server.gepa_job_api.eval_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_from_id", return_value=mock_eval, ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_eval", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_eval", params={"eval_id": eval_id}, ) @@ -1456,16 +1533,16 @@ def test_check_eval_config_not_found(client, mock_api_key, tmp_path): with ( patch( - "app.desktop.studio_server.gepa_job_api.eval_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_from_id", return_value=mock_eval, ), patch( - "app.desktop.studio_server.gepa_job_api.eval_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_config_from_id", side_effect=HTTPException(status_code=404, detail="Config not found"), ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_eval", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_eval", params={"eval_id": eval_id}, ) @@ -1502,16 +1579,16 @@ def test_check_eval_missing_model_name(client, mock_api_key, tmp_path): with ( patch( - "app.desktop.studio_server.gepa_job_api.eval_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_from_id", return_value=mock_eval, ), patch( - "app.desktop.studio_server.gepa_job_api.eval_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_config_from_id", return_value=mock_config, ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_eval", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_eval", params={"eval_id": eval_id}, ) @@ -1548,16 +1625,16 @@ def test_check_eval_missing_model_provider(client, mock_api_key, tmp_path): with ( patch( - "app.desktop.studio_server.gepa_job_api.eval_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_from_id", return_value=mock_eval, ), patch( - "app.desktop.studio_server.gepa_job_api.eval_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_config_from_id", return_value=mock_config, ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_eval", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_eval", params={"eval_id": eval_id}, ) @@ -1596,21 +1673,21 @@ def test_check_eval_server_validation_error(client, mock_api_key, tmp_path): with ( patch( - "app.desktop.studio_server.gepa_job_api.eval_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_from_id", return_value=mock_eval, ), patch( - "app.desktop.studio_server.gepa_job_api.eval_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_config_from_id", return_value=mock_config, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.check_model_supported_v1_jobs_gepa_job_check_model_supported_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.check_prompt_optimization_model_supported_v1_jobs_prompt_optimization_job_check_model_supported_get.asyncio", new_callable=AsyncMock, return_value=mock_error, ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_eval", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_eval", params={"eval_id": eval_id}, ) @@ -1643,21 +1720,21 @@ def test_check_eval_server_none_response(client, mock_api_key, tmp_path): with ( patch( - "app.desktop.studio_server.gepa_job_api.eval_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_from_id", return_value=mock_eval, ), patch( - "app.desktop.studio_server.gepa_job_api.eval_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_config_from_id", return_value=mock_config, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.check_model_supported_v1_jobs_gepa_job_check_model_supported_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.check_prompt_optimization_model_supported_v1_jobs_prompt_optimization_job_check_model_supported_get.asyncio", new_callable=AsyncMock, return_value=None, ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_eval", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_eval", params={"eval_id": eval_id}, ) @@ -1682,11 +1759,11 @@ def test_check_eval_exception(client, mock_api_key, tmp_path): eval_id = "test-eval-id" with patch( - "app.desktop.studio_server.gepa_job_api.eval_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_from_id", side_effect=Exception("Database error"), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_eval", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_eval", params={"eval_id": eval_id}, ) @@ -1729,21 +1806,21 @@ def test_check_eval_success_train_set( with ( patch( - "app.desktop.studio_server.gepa_job_api.eval_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_from_id", return_value=mock_eval, ), patch( - "app.desktop.studio_server.gepa_job_api.eval_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.eval_config_from_id", return_value=mock_config, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.check_model_supported_v1_jobs_gepa_job_check_model_supported_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.check_prompt_optimization_model_supported_v1_jobs_prompt_optimization_job_check_model_supported_get.asyncio", new_callable=AsyncMock, return_value=mock_check_response, ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_eval", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_eval", params={"eval_id": eval_id}, ) @@ -1754,8 +1831,10 @@ def test_check_eval_success_train_set( assert result["model_is_supported"] is True -def test_start_gepa_job_no_parent_project(client, mock_api_key, tmp_path): - """Test that start_gepa_job raises HTTPException when task has no parent.""" +def test_start_prompt_optimization_job_no_parent_project( + client, mock_api_key, tmp_path +): + """Test that start_prompt_optimization_job raises HTTPException when task has no parent.""" task = Task( name="Orphan Task", instruction="Test instruction", @@ -1765,11 +1844,11 @@ def test_start_gepa_job_no_parent_project(client, mock_api_key, tmp_path): task_id = "test-task-id" with patch( - "app.desktop.studio_server.gepa_job_api.task_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", return_value=task, ): response = client.post( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/start", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/start", json={ "target_run_config_id": "test-run-config-id", "eval_ids": [], @@ -1780,8 +1859,10 @@ def test_start_gepa_job_no_parent_project(client, mock_api_key, tmp_path): assert "Project not found" in response.json()["detail"] -def test_start_gepa_job_with_tools_in_run_config(client, mock_api_key, tmp_path): - """Test that start_gepa_job raises HTTPException when run config has tools.""" +def test_start_prompt_optimization_job_with_tools_in_run_config( + client, mock_api_key, tmp_path +): + """Test that start_prompt_optimization_job raises HTTPException when run config has tools.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() @@ -1801,16 +1882,16 @@ def test_start_gepa_job_with_tools_in_run_config(client, mock_api_key, tmp_path) with ( patch( - "app.desktop.studio_server.gepa_job_api.task_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", return_value=task, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ), ): response = client.post( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/start", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/start", json={ "target_run_config_id": "test-run-config-id", "eval_ids": [], @@ -1822,8 +1903,10 @@ def test_start_gepa_job_with_tools_in_run_config(client, mock_api_key, tmp_path) assert "tools" in response.json()["detail"] -def test_start_gepa_job_server_not_authenticated(client, mock_api_key, tmp_path): - """Test that start_gepa_job raises HTTPException when server client is not authenticated.""" +def test_start_prompt_optimization_job_server_not_authenticated( + client, mock_api_key, tmp_path +): + """Test that start_prompt_optimization_job raises HTTPException when server client is not authenticated.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() @@ -1842,20 +1925,20 @@ def test_start_gepa_job_server_not_authenticated(client, mock_api_key, tmp_path) with ( patch( - "app.desktop.studio_server.gepa_job_api.task_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", return_value=task, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.get_authenticated_client", + "app.desktop.studio_server.prompt_optimization_job_api.get_authenticated_client", return_value=MagicMock(spec=str), ), ): response = client.post( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/start", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/start", json={ "target_run_config_id": "test-run-config-id", "eval_ids": [], @@ -1866,8 +1949,10 @@ def test_start_gepa_job_server_not_authenticated(client, mock_api_key, tmp_path) assert "not authenticated" in response.json()["detail"] -def test_start_gepa_job_server_validation_error(client, mock_api_key, tmp_path): - """Test that start_gepa_job surfaces upstream validation errors via check_response_error.""" +def test_start_prompt_optimization_job_server_validation_error( + client, mock_api_key, tmp_path +): + """Test that start_prompt_optimization_job surfaces upstream validation errors via check_response_error.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() @@ -1886,19 +1971,19 @@ def test_start_gepa_job_server_validation_error(client, mock_api_key, tmp_path): with ( patch( - "app.desktop.studio_server.gepa_job_api.task_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", return_value=task, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.package_project_for_training", + "app.desktop.studio_server.prompt_optimization_job_api.package_project_for_training", side_effect=_mock_package_project_for_training, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.start_gepa_job_v1_jobs_gepa_job_start_post.asyncio_detailed", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.asyncio_detailed", new_callable=AsyncMock, return_value=_make_sdk_response( status_code=HTTPStatus.UNPROCESSABLE_ENTITY, @@ -1907,7 +1992,7 @@ def test_start_gepa_job_server_validation_error(client, mock_api_key, tmp_path): ), ): response = client.post( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/start", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/start", json={ "target_run_config_id": "test-run-config-id", "eval_ids": [], @@ -1918,8 +2003,10 @@ def test_start_gepa_job_server_validation_error(client, mock_api_key, tmp_path): assert "Upstream validation error" in response.json()["detail"] -def test_start_gepa_job_server_none_response(client, mock_api_key, tmp_path): - """Test that start_gepa_job handles None parsed response from server.""" +def test_start_prompt_optimization_job_server_none_response( + client, mock_api_key, tmp_path +): + """Test that start_prompt_optimization_job handles None parsed response from server.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() @@ -1938,25 +2025,25 @@ def test_start_gepa_job_server_none_response(client, mock_api_key, tmp_path): with ( patch( - "app.desktop.studio_server.gepa_job_api.task_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", return_value=task, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.package_project_for_training", + "app.desktop.studio_server.prompt_optimization_job_api.package_project_for_training", side_effect=_mock_package_project_for_training, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.start_gepa_job_v1_jobs_gepa_job_start_post.asyncio_detailed", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.asyncio_detailed", new_callable=AsyncMock, return_value=_make_sdk_response(parsed=None), ), ): response = client.post( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/start", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/start", json={ "target_run_config_id": "test-run-config-id", "eval_ids": [], @@ -1967,8 +2054,8 @@ def test_start_gepa_job_server_none_response(client, mock_api_key, tmp_path): assert "unexpected response from server" in response.json()["detail"] -def test_start_gepa_job_connection_error(client, mock_api_key, tmp_path): - """Test that start_gepa_job handles connection errors with specific message.""" +def test_start_prompt_optimization_job_connection_error(client, mock_api_key, tmp_path): + """Test that start_prompt_optimization_job handles connection errors with specific message.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() @@ -1990,25 +2077,25 @@ class ReadError(Exception): with ( patch( - "app.desktop.studio_server.gepa_job_api.task_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", return_value=task, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.package_project_for_training", + "app.desktop.studio_server.prompt_optimization_job_api.package_project_for_training", side_effect=_mock_package_project_for_training, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.start_gepa_job_v1_jobs_gepa_job_start_post.asyncio_detailed", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.asyncio_detailed", new_callable=AsyncMock, side_effect=ReadError("Connection lost"), ), ): response = client.post( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/start", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/start", json={ "target_run_config_id": "test-run-config-id", "eval_ids": [], @@ -2020,8 +2107,8 @@ class ReadError(Exception): assert "too large" in response.json()["detail"] -def test_start_gepa_job_timeout_error(client, mock_api_key, tmp_path): - """Test that start_gepa_job handles timeout errors with specific message.""" +def test_start_prompt_optimization_job_timeout_error(client, mock_api_key, tmp_path): + """Test that start_prompt_optimization_job handles timeout errors with specific message.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() @@ -2040,25 +2127,25 @@ def test_start_gepa_job_timeout_error(client, mock_api_key, tmp_path): with ( patch( - "app.desktop.studio_server.gepa_job_api.task_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", return_value=task, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.package_project_for_training", + "app.desktop.studio_server.prompt_optimization_job_api.package_project_for_training", side_effect=_mock_package_project_for_training, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.start_gepa_job_v1_jobs_gepa_job_start_post.asyncio_detailed", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.start_prompt_optimization_job_v1_jobs_prompt_optimization_job_start_post.asyncio_detailed", new_callable=AsyncMock, side_effect=Exception("Request timeout occurred"), ), ): response = client.post( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/start", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/start", json={ "target_run_config_id": "test-run-config-id", "eval_ids": [], @@ -2069,8 +2156,10 @@ def test_start_gepa_job_timeout_error(client, mock_api_key, tmp_path): assert "Connection error" in response.json()["detail"] -def test_start_gepa_job_general_exception(client, mock_api_key, tmp_path): - """Test that start_gepa_job handles general exceptions.""" +def test_start_prompt_optimization_job_general_exception( + client, mock_api_key, tmp_path +): + """Test that start_prompt_optimization_job handles general exceptions.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() @@ -2089,20 +2178,20 @@ def test_start_gepa_job_general_exception(client, mock_api_key, tmp_path): with ( patch( - "app.desktop.studio_server.gepa_job_api.task_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", return_value=task, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=mock_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.package_project_for_training", + "app.desktop.studio_server.prompt_optimization_job_api.package_project_for_training", side_effect=Exception("Unexpected error"), ), ): response = client.post( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/start", + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/start", json={ "target_run_config_id": "test-run-config-id", "eval_ids": [], @@ -2110,17 +2199,19 @@ def test_start_gepa_job_general_exception(client, mock_api_key, tmp_path): ) assert response.status_code == 500 - assert "Failed to start GEPA job" in response.json()["detail"] + assert "Failed to start Prompt Optimization job" in response.json()["detail"] -def test_gepa_job_creates_run_config_on_success(client, mock_api_key, tmp_path): - """Test that a run config is created when a GEPA job succeeds.""" +def test_prompt_optimization_job_creates_run_config_on_success( + client, mock_api_key, tmp_path +): + """Test that a run config is created when a Prompt Optimization job succeeds.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) @@ -2141,32 +2232,35 @@ def test_gepa_job_creates_run_config_on_success(client, mock_api_key, tmp_path): assert target_run_config.id is not None - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id=target_run_config.id, latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id - gepa_job_id = gepa_job.id + prompt_optimization_job_id = prompt_optimization_job.id - optimized_prompt = "This is the optimized prompt from GEPA" + optimized_prompt = "This is the optimized prompt from Prompt Optimization" mock_status_response = JobStatusResponse( job_id="remote-job-123", status=JobStatus.SUCCEEDED ) - mock_result_response = GEPAJobResultResponse( + mock_result_response = PromptOptimizationJobResultResponse( status=JobStatus.SUCCEEDED, - output=GEPAJobOutput(optimized_prompt=optimized_prompt), + output=PromptOptimizationJobOutput(optimized_prompt=optimized_prompt), ) with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=target_run_config, ), patch( @@ -2175,13 +2269,13 @@ def test_gepa_job_creates_run_config_on_success(client, mock_api_key, tmp_path): return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_result_response, ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response.status_code == 200 @@ -2193,7 +2287,7 @@ def test_gepa_job_creates_run_config_on_success(client, mock_api_key, tmp_path): prompts = task.prompts() assert len(prompts) == 1 assert prompts[0].prompt == optimized_prompt - assert prompts[0].name == gepa_job.name + assert prompts[0].name == prompt_optimization_job.name # Check that exactly 1 new run config was created (2 total including target) run_configs = task.run_configs() @@ -2217,18 +2311,20 @@ def test_gepa_job_creates_run_config_on_success(client, mock_api_key, tmp_path): == target_run_config.run_config_properties.model_provider_name ) - # Check that the gepa job has the created run config ID + # Check that the prompt optimization job has the created run config ID assert result["created_run_config_id"] == new_run_config.id -def test_gepa_job_only_creates_run_config_once(client, mock_api_key, tmp_path): +def test_prompt_optimization_job_only_creates_run_config_once( + client, mock_api_key, tmp_path +): """Test that a run config is only created once even if status is checked multiple times.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) @@ -2248,32 +2344,35 @@ def test_gepa_job_only_creates_run_config_once(client, mock_api_key, tmp_path): assert target_run_config.id is not None - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id=target_run_config.id, latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id - gepa_job_id = gepa_job.id + prompt_optimization_job_id = prompt_optimization_job.id - optimized_prompt = "This is the optimized prompt from GEPA" + optimized_prompt = "This is the optimized prompt from Prompt Optimization" mock_status_response = JobStatusResponse( job_id="remote-job-123", status=JobStatus.SUCCEEDED ) - mock_result_response = GEPAJobResultResponse( + mock_result_response = PromptOptimizationJobResultResponse( status=JobStatus.SUCCEEDED, - output=GEPAJobOutput(optimized_prompt=optimized_prompt), + output=PromptOptimizationJobOutput(optimized_prompt=optimized_prompt), ) with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=target_run_config, ), patch( @@ -2282,18 +2381,18 @@ def test_gepa_job_only_creates_run_config_once(client, mock_api_key, tmp_path): return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_result_response, ), ): response_1 = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response_1.status_code == 200 response_2 = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response_2.status_code == 200 @@ -2305,7 +2404,7 @@ def test_gepa_job_only_creates_run_config_once(client, mock_api_key, tmp_path): assert len(run_configs) == 2 -def test_gepa_job_run_config_handles_missing_target_config( +def test_prompt_optimization_job_run_config_handles_missing_target_config( client, mock_api_key, tmp_path ): """Test that run config creation handles missing target config gracefully.""" @@ -2314,49 +2413,52 @@ def test_gepa_job_run_config_handles_missing_target_config( task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id="nonexistent-config-id", latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id - gepa_job_id = gepa_job.id + prompt_optimization_job_id = prompt_optimization_job.id - optimized_prompt = "This is the optimized prompt from GEPA" + optimized_prompt = "This is the optimized prompt from Prompt Optimization" mock_status_response = JobStatusResponse( job_id="remote-job-123", status=JobStatus.SUCCEEDED ) - mock_result_response = GEPAJobResultResponse( + mock_result_response = PromptOptimizationJobResultResponse( status=JobStatus.SUCCEEDED, - output=GEPAJobOutput(optimized_prompt=optimized_prompt), + output=PromptOptimizationJobOutput(optimized_prompt=optimized_prompt), ) with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), patch( "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_job_status_v1_jobs_job_type_job_id_status_get.asyncio", new_callable=AsyncMock, return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_result_response, ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response.status_code == 200 @@ -2377,7 +2479,7 @@ def test_gepa_job_run_config_handles_missing_target_config( def test_cleanup_artifact_deletes_prompt_successfully(mock_api_key, tmp_path): """Test that _cleanup_artifact successfully deletes a prompt.""" - from app.desktop.studio_server.gepa_job_api import _cleanup_artifact + from app.desktop.studio_server.prompt_optimization_job_api import _cleanup_artifact project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() @@ -2416,7 +2518,7 @@ def test_cleanup_artifact_deletes_prompt_successfully(mock_api_key, tmp_path): def test_cleanup_artifact_deletes_run_config_successfully(mock_api_key, tmp_path): """Test that _cleanup_artifact successfully deletes a run config.""" - from app.desktop.studio_server.gepa_job_api import _cleanup_artifact + from app.desktop.studio_server.prompt_optimization_job_api import _cleanup_artifact project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() @@ -2435,7 +2537,7 @@ def test_cleanup_artifact_deletes_run_config_successfully(mock_api_key, tmp_path name="Test Config", run_config_properties=RunConfigProperties( model_name="gpt-4", - model_provider_name="openai", + model_provider_name=ModelProviderName.openai, prompt_id="simple_prompt_builder", structured_output_mode=StructuredOutputMode.default, ), @@ -2456,7 +2558,7 @@ def test_cleanup_artifact_deletes_run_config_successfully(mock_api_key, tmp_path def test_cleanup_artifact_handles_none_gracefully(mock_api_key): """Test that _cleanup_artifact handles None without error.""" - from app.desktop.studio_server.gepa_job_api import _cleanup_artifact + from app.desktop.studio_server.prompt_optimization_job_api import _cleanup_artifact # Should not raise an exception _cleanup_artifact(None, "prompt", "test-job-id") @@ -2464,7 +2566,7 @@ def test_cleanup_artifact_handles_none_gracefully(mock_api_key): def test_cleanup_artifact_handles_deletion_error_gracefully(mock_api_key, tmp_path): """Test that _cleanup_artifact logs but doesn't raise when deletion fails.""" - from app.desktop.studio_server.gepa_job_api import _cleanup_artifact + from app.desktop.studio_server.prompt_optimization_job_api import _cleanup_artifact project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() @@ -2497,7 +2599,7 @@ def test_cleanup_artifact_handles_deletion_error_gracefully(mock_api_key, tmp_pa _cleanup_artifact(prompt, "prompt", "test-job-id") -def test_gepa_job_cleanup_prompt_when_prompt_creation_fails( +def test_prompt_optimization_job_cleanup_prompt_when_prompt_creation_fails( client, mock_api_key, tmp_path ): """Test that artifacts are cleaned up when prompt creation fails.""" @@ -2506,53 +2608,56 @@ def test_gepa_job_cleanup_prompt_when_prompt_creation_fails( task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id="config-1", latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() project_id = project.id task_id = task.id - gepa_job_id = gepa_job.id + prompt_optimization_job_id = prompt_optimization_job.id - optimized_prompt = "This is the optimized prompt from GEPA" + optimized_prompt = "This is the optimized prompt from Prompt Optimization" mock_status_response = JobStatusResponse( job_id="remote-job-123", status=JobStatus.SUCCEEDED ) - mock_result_response = GEPAJobResultResponse( + mock_result_response = PromptOptimizationJobResultResponse( status=JobStatus.SUCCEEDED, - output=GEPAJobOutput(optimized_prompt=optimized_prompt), + output=PromptOptimizationJobOutput(optimized_prompt=optimized_prompt), ) with ( - patch("app.desktop.studio_server.gepa_job_api.task_from_id", return_value=task), + patch( + "app.desktop.studio_server.prompt_optimization_job_api.task_from_id", + return_value=task, + ), patch( "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_job_status_v1_jobs_job_type_job_id_status_get.asyncio", new_callable=AsyncMock, return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_result_response, ), patch( - "app.desktop.studio_server.gepa_job_api.create_prompt_from_optimization", + "app.desktop.studio_server.prompt_optimization_job_api.create_prompt_from_optimization", side_effect=Exception("Prompt creation failed"), ), ): response = client.get( - f"/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}" + f"/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}" ) assert response.status_code == 200 @@ -2571,7 +2676,7 @@ def test_gepa_job_cleanup_prompt_when_prompt_creation_fails( assert result["created_run_config_id"] is None -def test_gepa_job_cleanup_both_artifacts_when_run_config_fails_after_prompt_created( +def test_prompt_optimization_job_cleanup_both_artifacts_when_run_config_fails_after_prompt_created( mock_api_key, tmp_path ): """Test that both prompt and run config are cleaned up when run config creation fails.""" @@ -2580,7 +2685,7 @@ def test_gepa_job_cleanup_both_artifacts_when_run_config_fails_after_prompt_crea task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) @@ -2598,22 +2703,22 @@ def test_gepa_job_cleanup_both_artifacts_when_run_config_fails_after_prompt_crea ) target_run_config.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id=target_run_config.id, latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() - optimized_prompt = "This is the optimized prompt from GEPA" + optimized_prompt = "This is the optimized prompt from Prompt Optimization" mock_status_response = JobStatusResponse( job_id="remote-job-123", status=JobStatus.SUCCEEDED ) - mock_result_response = GEPAJobResultResponse( + mock_result_response = PromptOptimizationJobResultResponse( status=JobStatus.SUCCEEDED, - output=GEPAJobOutput(optimized_prompt=optimized_prompt), + output=PromptOptimizationJobOutput(optimized_prompt=optimized_prompt), ) mock_client = MagicMock(spec=AuthenticatedClient) @@ -2625,25 +2730,27 @@ def test_gepa_job_cleanup_both_artifacts_when_run_config_fails_after_prompt_crea return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_result_response, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=target_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.gepa_job_from_id", - return_value=gepa_job, + "app.desktop.studio_server.prompt_optimization_job_api.prompt_optimization_job_from_id", + return_value=prompt_optimization_job, ), patch( - "app.desktop.studio_server.gepa_job_api.create_run_config_from_optimization", + "app.desktop.studio_server.prompt_optimization_job_api.create_run_config_from_optimization", side_effect=Exception("Run config creation failed"), ), ): updated_job = asyncio.run( - update_gepa_job_and_create_artifacts(gepa_job, mock_client) + update_prompt_optimization_job_and_create_artifacts( + prompt_optimization_job, mock_client + ) ) # Job should show succeeded status @@ -2661,14 +2768,14 @@ def test_gepa_job_cleanup_both_artifacts_when_run_config_fails_after_prompt_crea assert updated_job.created_run_config_id is None -def test_gepa_job_retry_after_cleanup(mock_api_key, tmp_path): +def test_prompt_optimization_job_retry_after_cleanup(mock_api_key, tmp_path): """Test that artifact creation can be retried after cleanup from a previous failure.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) @@ -2686,22 +2793,22 @@ def test_gepa_job_retry_after_cleanup(mock_api_key, tmp_path): ) target_run_config.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id=target_run_config.id, latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() - optimized_prompt = "This is the optimized prompt from GEPA" + optimized_prompt = "This is the optimized prompt from Prompt Optimization" mock_status_response = JobStatusResponse( job_id="remote-job-123", status=JobStatus.SUCCEEDED ) - mock_result_response = GEPAJobResultResponse( + mock_result_response = PromptOptimizationJobResultResponse( status=JobStatus.SUCCEEDED, - output=GEPAJobOutput(optimized_prompt=optimized_prompt), + output=PromptOptimizationJobOutput(optimized_prompt=optimized_prompt), ) mock_client = MagicMock(spec=AuthenticatedClient) @@ -2714,21 +2821,23 @@ def test_gepa_job_retry_after_cleanup(mock_api_key, tmp_path): return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_result_response, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=target_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.create_run_config_from_optimization", + "app.desktop.studio_server.prompt_optimization_job_api.create_run_config_from_optimization", side_effect=Exception("Temporary failure"), ), ): updated_job = asyncio.run( - update_gepa_job_and_create_artifacts(gepa_job, mock_client) + update_prompt_optimization_job_and_create_artifacts( + prompt_optimization_job, mock_client + ) ) # Verify cleanup happened @@ -2738,8 +2847,10 @@ def test_gepa_job_retry_after_cleanup(mock_api_key, tmp_path): assert len(prompts) == 0 # Reload job from disk to simulate a fresh invocation - gepa_job_reloaded = GepaJob.from_id_and_parent_path(gepa_job.id, task.path) - assert gepa_job_reloaded.created_prompt_id is None + prompt_optimization_job_reloaded = PromptOptimizationJob.from_id_and_parent_path( + prompt_optimization_job.id, task.path + ) + assert prompt_optimization_job_reloaded.created_prompt_id is None # Second attempt: should succeed because cleanup cleared the IDs with ( @@ -2749,21 +2860,23 @@ def test_gepa_job_retry_after_cleanup(mock_api_key, tmp_path): return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_result_response, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=target_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.gepa_job_from_id", - return_value=gepa_job_reloaded, + "app.desktop.studio_server.prompt_optimization_job_api.prompt_optimization_job_from_id", + return_value=prompt_optimization_job_reloaded, ), ): updated_job = asyncio.run( - update_gepa_job_and_create_artifacts(gepa_job_reloaded, mock_client) + update_prompt_optimization_job_and_create_artifacts( + prompt_optimization_job_reloaded, mock_client + ) ) # Now artifacts should be created successfully @@ -2778,14 +2891,16 @@ def test_gepa_job_retry_after_cleanup(mock_api_key, tmp_path): assert len(run_configs) == 2 # Original + new -def test_gepa_job_prevents_race_condition_on_artifact_creation(mock_api_key, tmp_path): +def test_prompt_optimization_job_prevents_race_condition_on_artifact_creation( + mock_api_key, tmp_path +): """Test that concurrent updates don't create duplicate artifacts due to locking.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) @@ -2803,22 +2918,22 @@ def test_gepa_job_prevents_race_condition_on_artifact_creation(mock_api_key, tmp ) target_run_config.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id=target_run_config.id, latest_status="pending", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() - optimized_prompt = "This is the optimized prompt from GEPA" + optimized_prompt = "This is the optimized prompt from Prompt Optimization" mock_status_response = JobStatusResponse( job_id="remote-job-123", status=JobStatus.SUCCEEDED ) - mock_result_response = GEPAJobResultResponse( + mock_result_response = PromptOptimizationJobResultResponse( status=JobStatus.SUCCEEDED, - output=GEPAJobOutput(optimized_prompt=optimized_prompt), + output=PromptOptimizationJobOutput(optimized_prompt=optimized_prompt), ) mock_client = MagicMock(spec=AuthenticatedClient) @@ -2832,23 +2947,27 @@ async def concurrent_updates(): return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_result_response, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=target_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.gepa_job_from_id", - return_value=gepa_job, + "app.desktop.studio_server.prompt_optimization_job_api.prompt_optimization_job_from_id", + return_value=prompt_optimization_job, ), ): - # Call update_gepa_job_and_create_artifacts concurrently with asyncio.gather + # Call update_prompt_optimization_job_and_create_artifacts concurrently with asyncio.gather results = await asyncio.gather( - update_gepa_job_and_create_artifacts(gepa_job, mock_client), - update_gepa_job_and_create_artifacts(gepa_job, mock_client), + update_prompt_optimization_job_and_create_artifacts( + prompt_optimization_job, mock_client + ), + update_prompt_optimization_job_and_create_artifacts( + prompt_optimization_job, mock_client + ), ) return results @@ -2884,7 +3003,7 @@ async def concurrent_updates(): (JobStatus.PENDING, JobStatus.CANCELLED, False), ], ) -def test_update_gepa_job_status_transitions( +def test_update_prompt_optimization_job_status_transitions( mock_api_key, tmp_path, previous_status, new_status, should_create_artifacts ): """Test that artifacts are only created when transitioning TO succeeded from non-succeeded status.""" @@ -2893,7 +3012,7 @@ def test_update_gepa_job_status_transitions( task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) @@ -2912,20 +3031,20 @@ def test_update_gepa_job_status_transitions( target_run_config.save_to_file() # Create job with the previous_status - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id=target_run_config.id, latest_status=previous_status.value, parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() - optimized_prompt = "This is the optimized prompt from GEPA" + optimized_prompt = "This is the optimized prompt from Prompt Optimization" mock_status_response = JobStatusResponse(job_id="remote-job-123", status=new_status) - mock_result_response = GEPAJobResultResponse( + mock_result_response = PromptOptimizationJobResultResponse( status=new_status, - output=GEPAJobOutput(optimized_prompt=optimized_prompt), + output=PromptOptimizationJobOutput(optimized_prompt=optimized_prompt), ) mock_client = MagicMock(spec=AuthenticatedClient) @@ -2937,21 +3056,23 @@ def test_update_gepa_job_status_transitions( return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_result_response, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=target_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.gepa_job_from_id", - return_value=gepa_job, + "app.desktop.studio_server.prompt_optimization_job_api.prompt_optimization_job_from_id", + return_value=prompt_optimization_job, ), ): updated_job = asyncio.run( - update_gepa_job_and_create_artifacts(gepa_job, mock_client) + update_prompt_optimization_job_and_create_artifacts( + prompt_optimization_job, mock_client + ) ) # Verify status was updated @@ -2985,14 +3106,16 @@ def test_update_gepa_job_status_transitions( assert updated_job.created_run_config_id is None -def test_update_gepa_job_running_to_succeeded_creates_artifacts(mock_api_key, tmp_path): +def test_update_prompt_optimization_job_running_to_succeeded_creates_artifacts( + mock_api_key, tmp_path +): """Test that transitioning from running to succeeded creates artifacts.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) @@ -3011,22 +3134,22 @@ def test_update_gepa_job_running_to_succeeded_creates_artifacts(mock_api_key, tm target_run_config.save_to_file() # Job starts as RUNNING (not pending) - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id=target_run_config.id, latest_status=JobStatus.RUNNING.value, parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() optimized_prompt = "Optimized from running state" mock_status_response = JobStatusResponse( job_id="remote-job-123", status=JobStatus.SUCCEEDED ) - mock_result_response = GEPAJobResultResponse( + mock_result_response = PromptOptimizationJobResultResponse( status=JobStatus.SUCCEEDED, - output=GEPAJobOutput(optimized_prompt=optimized_prompt), + output=PromptOptimizationJobOutput(optimized_prompt=optimized_prompt), ) mock_client = MagicMock(spec=AuthenticatedClient) @@ -3038,21 +3161,23 @@ def test_update_gepa_job_running_to_succeeded_creates_artifacts(mock_api_key, tm return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, return_value=mock_result_response, ), patch( - "app.desktop.studio_server.gepa_job_api.task_run_config_from_id", + "app.desktop.studio_server.prompt_optimization_job_api.task_run_config_from_id", return_value=target_run_config, ), patch( - "app.desktop.studio_server.gepa_job_api.gepa_job_from_id", - return_value=gepa_job, + "app.desktop.studio_server.prompt_optimization_job_api.prompt_optimization_job_from_id", + return_value=prompt_optimization_job, ), ): updated_job = asyncio.run( - update_gepa_job_and_create_artifacts(gepa_job, mock_client) + update_prompt_optimization_job_and_create_artifacts( + prompt_optimization_job, mock_client + ) ) assert updated_job.latest_status == JobStatus.SUCCEEDED.value @@ -3067,21 +3192,23 @@ def test_update_gepa_job_running_to_succeeded_creates_artifacts(mock_api_key, tm assert len(run_configs) == 2 -def test_update_gepa_job_succeeded_to_succeeded_no_artifacts(mock_api_key, tmp_path): +def test_update_prompt_optimization_job_succeeded_to_succeeded_no_artifacts( + mock_api_key, tmp_path +): """Test that job already succeeded does not recreate artifacts.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() # Job is already succeeded with artifacts - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id="config-1", @@ -3091,7 +3218,7 @@ def test_update_gepa_job_succeeded_to_succeeded_no_artifacts(mock_api_key, tmp_p created_run_config_id="existing-run-config", parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() mock_status_response = JobStatusResponse( job_id="remote-job-123", status=JobStatus.SUCCEEDED @@ -3106,12 +3233,14 @@ def test_update_gepa_job_succeeded_to_succeeded_no_artifacts(mock_api_key, tmp_p return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, ) as mock_result, ): updated_job = asyncio.run( - update_gepa_job_and_create_artifacts(gepa_job, mock_client) + update_prompt_optimization_job_and_create_artifacts( + prompt_optimization_job, mock_client + ) ) # Should not fetch result since already succeeded @@ -3131,27 +3260,29 @@ def test_update_gepa_job_succeeded_to_succeeded_no_artifacts(mock_api_key, tmp_p assert len(run_configs) == 0 -def test_update_gepa_job_pending_to_running_no_artifacts(mock_api_key, tmp_path): +def test_update_prompt_optimization_job_pending_to_running_no_artifacts( + mock_api_key, tmp_path +): """Test that transitioning from pending to running does not create artifacts.""" project = Project(name="Test Project", path=tmp_path / "project.kiln") project.save_to_file() task = Task( name="Test Task", - description="Test task for GEPA", + description="Test task for Prompt Optimization", instruction="Test instruction", parent=project, ) task.save_to_file() - gepa_job = GepaJob( + prompt_optimization_job = PromptOptimizationJob( name="Test Job", job_id="remote-job-123", target_run_config_id="config-1", latest_status=JobStatus.PENDING.value, parent=task, ) - gepa_job.save_to_file() + prompt_optimization_job.save_to_file() mock_status_response = JobStatusResponse( job_id="remote-job-123", status=JobStatus.RUNNING @@ -3166,12 +3297,14 @@ def test_update_gepa_job_pending_to_running_no_artifacts(mock_api_key, tmp_path) return_value=mock_status_response, ), patch( - "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_gepa_job_result_v1_jobs_gepa_job_job_id_result_get.asyncio", + "app.desktop.studio_server.api_client.kiln_ai_server_client.api.jobs.get_prompt_optimization_job_result_v1_jobs_prompt_optimization_job_job_id_result_get.asyncio", new_callable=AsyncMock, ) as mock_result, ): updated_job = asyncio.run( - update_gepa_job_and_create_artifacts(gepa_job, mock_client) + update_prompt_optimization_job_and_create_artifacts( + prompt_optimization_job, mock_client + ) ) # Should not fetch result since not succeeded diff --git a/app/web_ui/src/lib/api_schema.d.ts b/app/web_ui/src/lib/api_schema.d.ts index 7c4fbda23..b8a9c740c 100644 --- a/app/web_ui/src/lib/api_schema.d.ts +++ b/app/web_ui/src/lib/api_schema.d.ts @@ -2264,7 +2264,7 @@ export interface paths { patch?: never; trace?: never; }; - "/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_run_config": { + "/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_run_config": { parameters: { query?: never; header?: never; @@ -2273,9 +2273,9 @@ export interface paths { }; /** * Check Run Config - * @description Check if a run config is valid for a GEPA job by validating the model is supported. + * @description Check if a run config is valid for a Prompt Optimization job by validating the model is supported. */ - get: operations["check_run_config_api_projects__project_id__tasks__task_id__gepa_jobs_check_run_config_get"]; + get: operations["check_run_config_api_projects__project_id__tasks__task_id__prompt_optimization_jobs_check_run_config_get"]; put?: never; post?: never; delete?: never; @@ -2284,7 +2284,7 @@ export interface paths { patch?: never; trace?: never; }; - "/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/check_eval": { + "/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/check_eval": { parameters: { query?: never; header?: never; @@ -2293,10 +2293,10 @@ export interface paths { }; /** * Check Eval - * @description Check if an eval is valid for a GEPA job. + * @description Check if an eval is valid for a Prompt Optimization job. * Validates that the eval has a default config and that the model is supported. */ - get: operations["check_eval_api_projects__project_id__tasks__task_id__gepa_jobs_check_eval_get"]; + get: operations["check_eval_api_projects__project_id__tasks__task_id__prompt_optimization_jobs_check_eval_get"]; put?: never; post?: never; delete?: never; @@ -2305,7 +2305,7 @@ export interface paths { patch?: never; trace?: never; }; - "/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/start": { + "/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/start": { parameters: { query?: never; header?: never; @@ -2315,18 +2315,18 @@ export interface paths { get?: never; put?: never; /** - * Start Gepa Job - * @description Start a GEPA job by zipping the project and sending it to the Kiln server. - * Creates and saves a GepaJob datamodel to track the job. + * Start Prompt Optimization Job + * @description Start a prompt optimization job by zipping the project and sending it to the Kiln server. + * Creates and saves a PromptOptimizationJob datamodel to track the job. */ - post: operations["start_gepa_job_api_projects__project_id__tasks__task_id__gepa_jobs_start_post"]; + post: operations["start_prompt_optimization_job_api_projects__project_id__tasks__task_id__prompt_optimization_jobs_start_post"]; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; - "/api/projects/{project_id}/tasks/{task_id}/gepa_jobs": { + "/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs": { parameters: { query?: never; header?: never; @@ -2334,11 +2334,11 @@ export interface paths { cookie?: never; }; /** - * List Gepa Jobs - * @description List all GEPA jobs for a task. + * List Prompt Optimization Jobs + * @description List all Prompt Optimization jobs for a task. * Optionally update the status of non-final jobs from the remote server. */ - get: operations["list_gepa_jobs_api_projects__project_id__tasks__task_id__gepa_jobs_get"]; + get: operations["list_prompt_optimization_jobs_api_projects__project_id__tasks__task_id__prompt_optimization_jobs_get"]; put?: never; post?: never; delete?: never; @@ -2347,7 +2347,7 @@ export interface paths { patch?: never; trace?: never; }; - "/api/projects/{project_id}/tasks/{task_id}/gepa_jobs/{gepa_job_id}": { + "/api/projects/{project_id}/tasks/{task_id}/prompt_optimization_jobs/{prompt_optimization_job_id}": { parameters: { query?: never; header?: never; @@ -2355,13 +2355,13 @@ export interface paths { cookie?: never; }; /** - * Get Gepa Job - * @description Get a specific GEPA job and update its status from the remote server. + * Get Prompt Optimization Job + * @description Get a specific Prompt Optimization job and update its status from the remote server. * If the job has succeeded, create a prompt if one doesn't exist yet. * If the job is already in a settled state (succeeded, failed, cancelled), * skip the status update and return the cached model. */ - get: operations["get_gepa_job_api_projects__project_id__tasks__task_id__gepa_jobs__gepa_job_id__get"]; + get: operations["get_prompt_optimization_job_api_projects__project_id__tasks__task_id__prompt_optimization_jobs__prompt_optimization_job_id__get"]; put?: never; post?: never; delete?: never; @@ -2370,7 +2370,7 @@ export interface paths { patch?: never; trace?: never; }; - "/api/gepa_jobs/{job_id}/status": { + "/api/prompt_optimization_jobs/{job_id}/status": { parameters: { query?: never; header?: never; @@ -2378,10 +2378,10 @@ export interface paths { cookie?: never; }; /** - * Get Gepa Job Status - * @description Get the status of a GEPA job. + * Get Prompt Optimization Job Status + * @description Get the status of a Prompt Optimization job. */ - get: operations["get_gepa_job_status_api_gepa_jobs__job_id__status_get"]; + get: operations["get_prompt_optimization_job_status_api_prompt_optimization_jobs__job_id__status_get"]; put?: never; post?: never; delete?: never; @@ -2390,7 +2390,7 @@ export interface paths { patch?: never; trace?: never; }; - "/api/gepa_jobs/{job_id}/result": { + "/api/prompt_optimization_jobs/{job_id}/result": { parameters: { query?: never; header?: never; @@ -2398,10 +2398,10 @@ export interface paths { cookie?: never; }; /** - * Get Gepa Job Result - * @description Get the result of a GEPA job (includes status and output if completed). + * Get Prompt Optimization Job Result + * @description Get the result of a prompt optimization job (includes status and output if completed). */ - get: operations["get_gepa_job_result_api_gepa_jobs__job_id__result_get"]; + get: operations["get_prompt_optimization_job_result_api_prompt_optimization_jobs__job_id__result_get"]; put?: never; post?: never; delete?: never; @@ -4769,76 +4769,6 @@ export interface components { [key: string]: components["schemas"]["SampleApi"][]; }; }; - /** - * GepaJob - * @description The Kiln GEPA job datamodel. - */ - GepaJob: { - /** - * V - * @default 1 - */ - v: number; - /** Id */ - id?: string | null; - /** Path */ - path?: string | null; - /** - * Created At - * Format: date-time - */ - created_at?: string; - /** Created By */ - created_by?: string; - /** - * Name - * @description The name of the GEPA job. - */ - name: string; - /** - * Description - * @description A description of the GEPA job for you and your team. - */ - description?: string | null; - /** - * Job Id - * @description The ID of the job on the remote Kiln server. - */ - job_id: string; - /** - * Target Run Config Id - * @description The ID of the run configuration used for this job. - */ - target_run_config_id: string; - /** - * Latest Status - * @description The latest known status of this GEPA job (pending, running, succeeded, failed, cancelled). Not updated in real time. - * @default pending - */ - latest_status: string; - /** - * Optimized Prompt - * @description The optimized prompt result when the job succeeds. - */ - optimized_prompt?: string | null; - /** - * Created Prompt Id - * @description The ID of the prompt created from this job's result, if any. - */ - created_prompt_id?: string | null; - /** - * Created Run Config Id - * @description The ID of the run config created from this job's result, if any. - */ - created_run_config_id?: string | null; - /** - * Eval Ids - * @description List of eval IDs used for this job. - */ - eval_ids?: string[]; - /** Model Type */ - readonly model_type: string; - }; /** GetRagConfigProgressRequest */ GetRagConfigProgressRequest: { /** @@ -5515,6 +5445,76 @@ export interface components { /** Leakage Examples */ leakage_examples: string; }; + /** + * PromptOptimizationJob + * @description The Kiln prompt optimization job datamodel. + */ + PromptOptimizationJob: { + /** + * V + * @default 1 + */ + v: number; + /** Id */ + id?: string | null; + /** Path */ + path?: string | null; + /** + * Created At + * Format: date-time + */ + created_at?: string; + /** Created By */ + created_by?: string; + /** + * Name + * @description The name of the prompt optimization job. + */ + name: string; + /** + * Description + * @description A description of the prompt optimization job for you and your team. + */ + description?: string | null; + /** + * Job Id + * @description The ID of the job on the remote Kiln server. + */ + job_id: string; + /** + * Target Run Config Id + * @description The ID of the run configuration used for this job. + */ + target_run_config_id: string; + /** + * Latest Status + * @description The latest known status of this prompt optimization job (pending, running, succeeded, failed, cancelled). Not updated in real time. + * @default pending + */ + latest_status: string; + /** + * Optimized Prompt + * @description The optimized prompt result when the job succeeds. + */ + optimized_prompt?: string | null; + /** + * Created Prompt Id + * @description The ID of the prompt created from this job's result, if any. + */ + created_prompt_id?: string | null; + /** + * Created Run Config Id + * @description The ID of the run config created from this job's result, if any. + */ + created_run_config_id?: string | null; + /** + * Eval Ids + * @description List of eval IDs used for this job. + */ + eval_ids?: string[]; + /** Model Type */ + readonly model_type: string; + }; /** PromptResponse */ PromptResponse: { /** Generators */ @@ -5558,18 +5558,18 @@ export interface components { }; }; /** - * PublicGEPAJobResultResponse - * @description Public response model for GEPA job result containing only the optimized prompt. + * PublicPromptOptimizationJobResultResponse + * @description Public response model for prompt optimization job result containing only the optimized prompt. */ - PublicGEPAJobResultResponse: { + PublicPromptOptimizationJobResultResponse: { /** Optimized Prompt */ optimized_prompt: string; }; /** - * PublicGEPAJobStatusResponse - * @description Public response model for GEPA job status. + * PublicPromptOptimizationJobStatusResponse + * @description Public response model for prompt optimization job status. */ - PublicGEPAJobStatusResponse: { + PublicPromptOptimizationJobStatusResponse: { /** Job Id */ job_id: string; status: components["schemas"]["JobStatus"]; @@ -6353,8 +6353,8 @@ export interface components { [key: string]: string; }; }; - /** StartGepaJobRequest */ - StartGepaJobRequest: { + /** StartPromptOptimizationJobRequest */ + StartPromptOptimizationJobRequest: { /** Target Run Config Id */ target_run_config_id: string; /** Eval Ids */ @@ -12144,7 +12144,7 @@ export interface operations { }; }; }; - check_run_config_api_projects__project_id__tasks__task_id__gepa_jobs_check_run_config_get: { + check_run_config_api_projects__project_id__tasks__task_id__prompt_optimization_jobs_check_run_config_get: { parameters: { query: { run_config_id: string; @@ -12178,7 +12178,7 @@ export interface operations { }; }; }; - check_eval_api_projects__project_id__tasks__task_id__gepa_jobs_check_eval_get: { + check_eval_api_projects__project_id__tasks__task_id__prompt_optimization_jobs_check_eval_get: { parameters: { query: { eval_id: string; @@ -12212,7 +12212,7 @@ export interface operations { }; }; }; - start_gepa_job_api_projects__project_id__tasks__task_id__gepa_jobs_start_post: { + start_prompt_optimization_job_api_projects__project_id__tasks__task_id__prompt_optimization_jobs_start_post: { parameters: { query?: never; header?: never; @@ -12224,7 +12224,7 @@ export interface operations { }; requestBody: { content: { - "application/json": components["schemas"]["StartGepaJobRequest"]; + "application/json": components["schemas"]["StartPromptOptimizationJobRequest"]; }; }; responses: { @@ -12234,7 +12234,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["GepaJob"]; + "application/json": components["schemas"]["PromptOptimizationJob"]; }; }; /** @description Validation Error */ @@ -12248,7 +12248,7 @@ export interface operations { }; }; }; - list_gepa_jobs_api_projects__project_id__tasks__task_id__gepa_jobs_get: { + list_prompt_optimization_jobs_api_projects__project_id__tasks__task_id__prompt_optimization_jobs_get: { parameters: { query?: { update_status?: boolean; @@ -12268,7 +12268,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["GepaJob"][]; + "application/json": components["schemas"]["PromptOptimizationJob"][]; }; }; /** @description Validation Error */ @@ -12282,14 +12282,14 @@ export interface operations { }; }; }; - get_gepa_job_api_projects__project_id__tasks__task_id__gepa_jobs__gepa_job_id__get: { + get_prompt_optimization_job_api_projects__project_id__tasks__task_id__prompt_optimization_jobs__prompt_optimization_job_id__get: { parameters: { query?: never; header?: never; path: { project_id: string; task_id: string; - gepa_job_id: string; + prompt_optimization_job_id: string; }; cookie?: never; }; @@ -12301,7 +12301,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["GepaJob"]; + "application/json": components["schemas"]["PromptOptimizationJob"]; }; }; /** @description Validation Error */ @@ -12315,7 +12315,7 @@ export interface operations { }; }; }; - get_gepa_job_status_api_gepa_jobs__job_id__status_get: { + get_prompt_optimization_job_status_api_prompt_optimization_jobs__job_id__status_get: { parameters: { query?: never; header?: never; @@ -12332,7 +12332,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PublicGEPAJobStatusResponse"]; + "application/json": components["schemas"]["PublicPromptOptimizationJobStatusResponse"]; }; }; /** @description Validation Error */ @@ -12346,7 +12346,7 @@ export interface operations { }; }; }; - get_gepa_job_result_api_gepa_jobs__job_id__result_get: { + get_prompt_optimization_job_result_api_prompt_optimization_jobs__job_id__result_get: { parameters: { query?: never; header?: never; @@ -12363,7 +12363,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["PublicGEPAJobResultResponse"]; + "application/json": components["schemas"]["PublicPromptOptimizationJobResultResponse"]; }; }; /** @description Validation Error */ diff --git a/app/web_ui/src/lib/types.ts b/app/web_ui/src/lib/types.ts index 76853bcef..f47f8dcfc 100644 --- a/app/web_ui/src/lib/types.ts +++ b/app/web_ui/src/lib/types.ts @@ -164,9 +164,10 @@ export type QuestionWithAnswer = components["schemas"]["QuestionWithAnswer"] export type AnswerOptionWithSelection = components["schemas"]["AnswerOptionWithSelection"] -export type PublicGEPAJobStatusResponse = - components["schemas"]["PublicGEPAJobStatusResponse"] -export type GepaJob = components["schemas"]["GepaJob"] +export type PublicPromptOptimizationJobStatusResponse = + components["schemas"]["PublicPromptOptimizationJobStatusResponse"] +export type PromptOptimizationJob = + components["schemas"]["PromptOptimizationJob"] export type JobStatus = components["schemas"]["JobStatus"] // Type helpers for ExternalToolServerApiDescription properties diff --git a/app/web_ui/src/lib/ui/kiln_copilot/copilot_required_card.svelte b/app/web_ui/src/lib/ui/kiln_copilot/copilot_required_card.svelte index 62c1f66c2..c0d307fa5 100644 --- a/app/web_ui/src/lib/ui/kiln_copilot/copilot_required_card.svelte +++ b/app/web_ui/src/lib/ui/kiln_copilot/copilot_required_card.svelte @@ -11,7 +11,7 @@ action_buttons={[ { label: "Connect Kiln Copilot", - href: "/gepa/copilot_auth", + href: "/prompt_optimization/copilot_auth", is_primary: true, }, ]} diff --git a/app/web_ui/src/routes/(app)/gepa/[project_id]/[task_id]/+page.svelte b/app/web_ui/src/routes/(app)/prompt_optimization/[project_id]/[task_id]/+page.svelte similarity index 69% rename from app/web_ui/src/routes/(app)/gepa/[project_id]/[task_id]/+page.svelte rename to app/web_ui/src/routes/(app)/prompt_optimization/[project_id]/[task_id]/+page.svelte index cadef10eb..2f3493656 100644 --- a/app/web_ui/src/routes/(app)/gepa/[project_id]/[task_id]/+page.svelte +++ b/app/web_ui/src/routes/(app)/prompt_optimization/[project_id]/[task_id]/+page.svelte @@ -1,7 +1,7 @@