Skip to content

Synth data goal #422

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 13, 2025
Merged

Synth data goal #422

merged 4 commits into from
Jul 13, 2025

Conversation

scosman
Copy link
Collaborator

@scosman scosman commented Jul 13, 2025

Require the user specify a goal before starting synth data gen.

Summary by CodeRabbit

  • New Features

    • Introduced an interactive UI for synthetic data generation, including dialogs for selecting evals and fine-tuning datasets with dynamic data fetching and error handling.
    • Added a reusable introductory UI block with customizable titles, descriptions, and action buttons.
    • Sidebar navigation improved by consolidating the "Evals" menu item.
  • Refactor

    • Split handling logic was moved from a UI component to utility functions, streamlining the process and removing the direct splits interface from relevant pages.
    • Centralized split parsing and setup logic for synthetic data generation.
  • Bug Fixes

    • Removed duplicated "Evals" menu entry in the sidebar navigation.
  • Tests

    • Updated tests to align with changes in the synthetic data guidance data model’s method signatures.

Copy link
Contributor

coderabbitai bot commented Jul 13, 2025

Walkthrough

A new Intro UI component was added, and the Splits Svelte component was removed. Splits parsing and subtitle logic were refactored into a utility module, with affected pages updated to use these utilities instead of the Splits UI. Synthetic data generation flows were made more interactive with dialogs, API integration, and improved state management.

Changes

File(s) Change Summary
app/web_ui/src/lib/ui/intro.svelte Added new Intro Svelte component for displaying a title, descriptions, and action buttons.
app/web_ui/src/lib/ui/splits.svelte Removed Splits Svelte component and its exports (splits, subtitle, get_random_split_tag).
app/web_ui/src/lib/utils/splits_util.ts Added utility functions: get_splits_from_url_param and get_splits_subtitle for parsing and describing splits.
app/web_ui/src/routes/(app)/+layout.svelte Removed duplicate "Evals" menu item from sidebar navigation.
app/web_ui/src/routes/(app)/dataset/[project_id]/[task_id]/add_data/+page.svelte Replaced Splits component with splits utility functions; removed Splits UI from the page.
app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/+page.svelte Refactored split handling: removed Splits component, added setup and get_random_split_tag functions, updated UI.
app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/data_gen_intro.svelte Major refactor: added dialogs, async API calls, new props, interactive synthetic data generation UI.
app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/synth_data_guidance_datamodel.test.ts Updated tests to include new splits parameter in load method calls.
app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/synth_data_guidance_datamodel.ts Added splits property and parameter to SynthDataGuidanceDataModel; updated load method signature and logic.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DataGenIntro
    participant API
    participant GuidanceDataModel

    User->>DataGenIntro: Click "Generate Synthetic Eval Data"
    DataGenIntro->>API: Fetch evals
    API-->>DataGenIntro: Return evals list
    User->>DataGenIntro: Select eval
    DataGenIntro->>GuidanceDataModel: on_setup("eval", template_id, eval_id, project_id, task_id, splits)
    GuidanceDataModel->>GuidanceDataModel: load(..., splits)
Loading
sequenceDiagram
    participant User
    participant DataGenIntro
    participant API
    participant GuidanceDataModel

    User->>DataGenIntro: Click "Generate Fine-tuning Data"
    DataGenIntro->>API: Fetch fine-tune dataset info
    API-->>DataGenIntro: Return dataset info
    User->>DataGenIntro: Select tag (if multiple)
    DataGenIntro->>GuidanceDataModel: on_setup("training", template_id, null, project_id, task_id, splits)
    GuidanceDataModel->>GuidanceDataModel: load(..., splits)
Loading

Poem

A bunny hopped through UI fields anew,
Splits now parsed with a clever view.
Dialogs pop and guide with care,
While action buttons dance in the air.
Old components bid their fond adieu—
New flows and models, fresh as dew!
🐇✨


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
app/web_ui/src/routes/(app)/+layout.svelte (1)

220-267: Replace hard-coded SVG stroke color to honor UI themes

The new “Evals” icon uses stroke="#1C274C" while other icons rely on currentColor. The hard-coded hex overrides DaisyUI / Tailwind theme colors and will look out of place in dark-mode or custom themes.

-              stroke="#1C274C"
+              stroke="currentColor"

Apply the same change to all four occurrences inside this icon to keep styling consistent and theme-aware.

app/web_ui/src/routes/(app)/dataset/[project_id]/[task_id]/add_data/+page.svelte (1)

18-23: Consider removing unnecessary tick() call

The tick() call appears unnecessary here since you're only reading from $page.url.searchParams, which doesn't require waiting for DOM updates.

  onMount(async () => {
-   await tick()
    const splitsParam = $page.url.searchParams.get("splits")
    splits = get_splits_from_url_param(splitsParam)
    splits_subtitle = get_splits_subtitle(splits)
  })
app/web_ui/src/lib/ui/intro.svelte (1)

5-11: Consider making href and onClick mutually exclusive

The current ActionButton type allows both href and onClick to be defined simultaneously, which could lead to confusion about which action takes precedence.

Consider using a discriminated union type:

  type ActionButton = {
    label: string
-   href?: string
-   new_tab?: boolean
-   onClick?: () => void
    is_primary: boolean
+  } & (
+   | { href: string; new_tab?: boolean }
+   | { onClick: () => void }
+  )
app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/+page.svelte (1)

117-142: Consider stronger error handling for duplicate setup calls.

While the guard against multiple setup calls is good, using only console.error might not be sufficient. Consider:

  • Throwing an error or showing a user-visible warning
  • Logging this to error tracking if available
  • Returning early to prevent any side effects
 function setup(
   gen_type: "training" | "eval",
   template_id: string | null,
   eval_id: string | null,
   project_id: string,
   task_id: string,
   splits: Record<string, number>,
 ) {
   if (!gen_type || !task) {
     return
   }
   if (is_setup) {
-    console.error("Setup already called. This should not happen.")
+    const error = new Error("Setup already called. This should not happen.")
+    console.error(error)
+    // Consider: task_error = createKilnError(error)
+    return
   }
   is_setup = true
   guidance_data.load(
     template_id,
     eval_id,
     project_id,
     task_id,
     gen_type,
     task,
     splits,
   )
   splits_subtitle = get_splits_subtitle(splits)
 }
app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/data_gen_intro.svelte (1)

219-223: Consider responsive design for grid layout.

The grid uses fixed pixel widths (270px) which may cause issues on smaller screens.

 <div
   class="grid grid-cols-2 gap-x-32 gap-y-4 items-start font-light text-sm"
-  style="grid-template-columns: 270px 270px;"
+  style="grid-template-columns: repeat(2, minmax(270px, 1fr));"
 >

Alternatively, consider using Tailwind's responsive grid classes:

-<div
-  class="grid grid-cols-2 gap-x-32 gap-y-4 items-start font-light text-sm"
-  style="grid-template-columns: 270px 270px;"
->
+<div class="grid grid-cols-1 md:grid-cols-2 gap-x-8 md:gap-x-32 gap-y-4 items-start font-light text-sm max-w-[600px]">
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f74f31 and 121622b.

📒 Files selected for processing (9)
  • app/web_ui/src/lib/ui/intro.svelte (1 hunks)
  • app/web_ui/src/lib/ui/splits.svelte (0 hunks)
  • app/web_ui/src/lib/utils/splits_util.ts (1 hunks)
  • app/web_ui/src/routes/(app)/+layout.svelte (1 hunks)
  • app/web_ui/src/routes/(app)/dataset/[project_id]/[task_id]/add_data/+page.svelte (1 hunks)
  • app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/+page.svelte (6 hunks)
  • app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/data_gen_intro.svelte (1 hunks)
  • app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/synth_data_guidance_datamodel.test.ts (7 hunks)
  • app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/synth_data_guidance_datamodel.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • app/web_ui/src/lib/ui/splits.svelte
🧰 Additional context used
🧠 Learnings (2)
app/web_ui/src/lib/utils/splits_util.ts (1)
Learnt from: scosman
PR: Kiln-AI/Kiln#296
File: libs/core/kiln_ai/utils/dataset_import.py:116-152
Timestamp: 2025-05-09T17:33:29.787Z
Learning: The tag_splits implementation in dataset_import.py uses deterministic allocation via integer division with remaining items assigned to the largest split, which is preferable over probability-based approaches to guarantee no splits are starved, especially with small datasets.
app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/+page.svelte (1)
Learnt from: scosman
PR: Kiln-AI/Kiln#296
File: libs/core/kiln_ai/utils/dataset_import.py:116-152
Timestamp: 2025-05-09T17:33:29.787Z
Learning: The tag_splits implementation in dataset_import.py uses deterministic allocation via integer division with remaining items assigned to the largest split, which is preferable over probability-based approaches to guarantee no splits are starved, especially with small datasets.
🔇 Additional comments (16)
app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/synth_data_guidance_datamodel.test.ts (1)

90-90: LGTM: Test method signature updates are consistent

All test calls to the load method have been correctly updated to include the new splits parameter with appropriate default values. The changes maintain existing test behavior while accommodating the updated API.

Also applies to: 99-107, 113-121, 143-151, 180-188, 206-214, 225-233

app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/synth_data_guidance_datamodel.ts (2)

22-22: LGTM: Clean addition of splits property

The new splits property is properly typed and initialized with a sensible default value.


61-61: LGTM: Method signature and assignment are correct

The load method signature is properly updated to include the splits parameter, and the assignment correctly stores the splits data in the instance property.

Also applies to: 68-68

app/web_ui/src/routes/(app)/dataset/[project_id]/[task_id]/add_data/+page.svelte (1)

9-12: LGTM: Clean import transition to utility functions

The import change properly replaces the Splits UI component with the new utility functions, aligning with the refactoring objectives.

app/web_ui/src/lib/utils/splits_util.ts (2)

1-28: LGTM: Robust parsing with proper validation

The get_splits_from_url_param function implements comprehensive validation including:

  • Range validation (0-1) for split values
  • Sum validation with appropriate floating-point tolerance
  • Graceful error handling with console warnings

The error handling approach of returning an empty object is appropriate for URL parameter parsing.


30-35: LGTM: Clean formatting function

The get_splits_subtitle function provides a clear, human-readable format for displaying split percentages. The conditional return for empty splits is well-handled.

app/web_ui/src/lib/ui/intro.svelte (1)

16-49: LGTM: Well-structured reusable component

The component provides a clean, flexible interface for intro screens with proper slot usage and conditional button rendering. The styling is consistent and the layout is well-organized.

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

22-25: LGTM! Good modularization of split utilities.

Moving split parsing logic to a dedicated utility module improves maintainability and reusability.


49-49: Verify setup state initialization.

The is_setup flag is initialized to false, which is correct. However, ensure that the setup process is idempotent in case of component re-mounts or navigation edge cases.


103-115: Well-structured URL parameter handling.

The implementation correctly:

  • Checks for required reason parameter
  • Handles optional parameters (eval_id, template_id, splits)
  • Uses the utility function for split parsing
  • Triggers setup only for valid generation types

306-307: Good integration of split tag selection.

The implementation correctly uses the get_random_split_tag function to assign split tags when saving samples, with proper handling of undefined returns.


410-411: Clean component integration with setup flow.

The binding of on_setup callback and is_setup state to the DataGenIntro component is well-implemented, enabling proper communication between parent and child components.

app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/data_gen_intro.svelte (4)

2-6: LGTM! Well-organized imports.

Good separation of concerns with proper imports for UI components, API client, error handling, and types.


12-23: Well-designed component interface.

The export interface properly:

  • Maintains backward compatibility with optional callback
  • Provides comprehensive type safety for the setup callback
  • Uses proper two-way binding for is_setup state

86-149: Excellent implementation of fine-tuning data flow.

The implementation has several good design choices:

  • Auto-triggers when only the default tag exists, improving UX
  • Properly sorts tags by count for better visibility
  • Always includes the default fine_tune_data tag
  • Clean error handling pattern

385-481: Well-structured dialog implementations.

Both dialogs demonstrate excellent UX patterns:

  • Clear loading states with spinners
  • User-friendly error messages
  • Intuitive card-based selection for evals
  • Smart button styling to highlight recommended options
  • Proper empty state handling

Comment on lines +355 to +371
function get_random_split_tag() {
const splits = guidance_data.splits
if (Object.keys(splits).length === 0) return undefined

const random = Math.random()
let cumulative = 0

for (const [tag, probability] of Object.entries(splits)) {
cumulative += probability
if (random <= cumulative) {
return tag
}
}

// Fallback (should never reach here if splits sum to 1)
return Object.keys(splits)[0]
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation for split probabilities sum.

The weighted random selection implementation is correct, but it assumes splits sum to 1.0. If they don't, the selection might be biased.

 function get_random_split_tag() {
   const splits = guidance_data.splits
   if (Object.keys(splits).length === 0) return undefined

+  // Validate that splits sum to approximately 1.0
+  const sum = Object.values(splits).reduce((a, b) => a + b, 0)
+  if (Math.abs(sum - 1.0) > 0.001) {
+    console.warn(`Split probabilities sum to ${sum}, expected 1.0`)
+  }
+
   const random = Math.random()
   let cumulative = 0

   for (const [tag, probability] of Object.entries(splits)) {
     cumulative += probability
     if (random <= cumulative) {
       return tag
     }
   }

   // Fallback (should never reach here if splits sum to 1)
   return Object.keys(splits)[0]
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function get_random_split_tag() {
const splits = guidance_data.splits
if (Object.keys(splits).length === 0) return undefined
const random = Math.random()
let cumulative = 0
for (const [tag, probability] of Object.entries(splits)) {
cumulative += probability
if (random <= cumulative) {
return tag
}
}
// Fallback (should never reach here if splits sum to 1)
return Object.keys(splits)[0]
}
function get_random_split_tag() {
const splits = guidance_data.splits
if (Object.keys(splits).length === 0) return undefined
// Validate that splits sum to approximately 1.0
const sum = Object.values(splits).reduce((a, b) => a + b, 0)
if (Math.abs(sum - 1.0) > 0.001) {
console.warn(`Split probabilities sum to ${sum}, expected 1.0`)
}
const random = Math.random()
let cumulative = 0
for (const [tag, probability] of Object.entries(splits)) {
cumulative += probability
if (random <= cumulative) {
return tag
}
}
// Fallback (should never reach here if splits sum to 1)
return Object.keys(splits)[0]
}
🤖 Prompt for AI Agents
In app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/+page.svelte
around lines 355 to 371, the function get_random_split_tag assumes that the
probabilities in splits sum to 1. Add validation to check if the sum of all
split probabilities is close to 1 before performing the weighted random
selection. If the sum is not close to 1, log a warning or handle the error
appropriately to avoid biased selection.

Comment on lines +60 to +84
function select_eval(evaluator: Eval) {
const eval_set_filter_id = evaluator.eval_set_filter_id
const eval_configs_filter_id = evaluator.eval_configs_filter_id
const splits: Record<string, number> = {}
if (
eval_set_filter_id.startsWith("tag::") &&
eval_configs_filter_id.startsWith("tag::")
) {
const eval_set_tag = eval_set_filter_id.split("::")[1]
const eval_configs_tag = eval_configs_filter_id.split("::")[1]
splits[eval_set_tag] = 0.8
splits[eval_configs_tag] = 0.2
} else {
alert(
"We can't generate synthetic data for this eval as it's eval sets are not defined by tag filters. Select an eval which uses tags to define eval sets.",
)
return
}
const eval_id = project_id + "::" + task_id + "::" + (evaluator.id ?? "")
const template_id = evaluator.template ?? null

on_setup?.("eval", template_id, eval_id, project_id, task_id, splits)

evals_dialog?.close()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve UX for non-tag-based eval handling.

The current implementation uses alert() for non-tag-based evals, which provides poor UX. Also, the 80/20 split ratio is hardcoded.

 function select_eval(evaluator: Eval) {
   const eval_set_filter_id = evaluator.eval_set_filter_id
   const eval_configs_filter_id = evaluator.eval_configs_filter_id
   const splits: Record<string, number> = {}
   if (
     eval_set_filter_id.startswith("tag::") &&
     eval_configs_filter_id.startsWith("tag::")
   ) {
     const eval_set_tag = eval_set_filter_id.split("::")[1]
     const eval_configs_tag = eval_configs_filter_id.split("::")[1]
-    splits[eval_set_tag] = 0.8
-    splits[eval_configs_tag] = 0.2
+    // Consider making these configurable or document why 80/20
+    const EVAL_SET_RATIO = 0.8
+    const EVAL_CONFIG_RATIO = 0.2
+    splits[eval_set_tag] = EVAL_SET_RATIO
+    splits[eval_configs_tag] = EVAL_CONFIG_RATIO
   } else {
-    alert(
-      "We can't generate synthetic data for this eval as it's eval sets are not defined by tag filters. Select an eval which uses tags to define eval sets.",
-    )
+    // Show inline error message instead of alert
+    evals_error = new KilnError(
+      "We can't generate synthetic data for this eval as it's eval sets are not defined by tag filters. Select an eval which uses tags to define eval sets."
+    )
     return
   }
   const eval_id = project_id + "::" + task_id + "::" + (evaluator.id ?? "")
   const template_id = evaluator.template ?? null

   on_setup?.("eval", template_id, eval_id, project_id, task_id, splits)

   evals_dialog?.close()
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
app/web_ui/src/routes/(app)/generate/[project_id]/[task_id]/data_gen_intro.svelte
between lines 60 and 84, replace the alert() call for non-tag-based evals with a
more user-friendly UI notification or modal to improve UX. Additionally, remove
the hardcoded 80/20 split ratio and instead make the split values configurable
or derived dynamically to allow flexibility.

@scosman scosman merged commit 12a7d7c into main Jul 13, 2025
14 checks passed
@scosman scosman deleted the synth_data_goal branch July 13, 2025 15:27
@coderabbitai coderabbitai bot mentioned this pull request Jul 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant