Skip to content
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

Ai Style Improvements #3424

Merged
merged 9 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 54 additions & 30 deletions packages/ai/src/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,60 +24,84 @@ export type PromptPair = {

export const changeFormatPrompt = `
**Response Format:**

Your response **must** include:

1. **Brief Explanation**: A very short explanation (4-15 words) of the root cause of the error and your fix, placed inside an \`<explanation>\` block.

2. **Minimal Code Change Block**: The smallest possible code change needed to fix the error, provided inside an \`<edit>\` block.

**Instructions:**

- Do **not** include any content outside the \`<explanation>\` and \`<edit>\` blocks.

- In the \`<edit>\` block:
- Start with \`<<<<<<< SEARCH\`.
- Include **only** the exact lines to be replaced from the original code. Copy them exactly, including comments and whitespace.
- Use \`=======\` as a divider.
- Provide the corrected code in the replace section.
- End with \`>>>>>>> REPLACE\`.
- Ensure the **SEARCH** section matches the original code **exactly**.
- Keep the code change minimal—avoid adding extra code or making unrelated changes.
- **Every** \`SEARCH\` section must **exactly match** the existing file content, character for character, including all comments, docstrings, etc.
- \`SEARCH/REPLACE\` blocks will replace **all** matching occurrences. Include enough lines to make the \`SEARCH\` blocks uniquely match the lines to change.
- To move code within a file, use 2 \`SEARCH/REPLACE\` blocks: one to delete it from its current location, and one to insert it in the new location.
- For each change, create a separate SEARCH/REPLACE block:
- Start with \`<<<<<<< SEARCH\`
- Include the exact lines to be replaced
- Use \`=======\` as a divider
- Provide the corrected code
- End with \`>>>>>>> REPLACE\`
- **Critical for Moving Code:**
- When moving code, you need TWO separate SEARCH/REPLACE blocks in this order:
1. First block REMOVES the code from its original location
2. Second block ADDS it to its new location
- Include enough surrounding context in each block to uniquely identify the location
- Remember that blocks are processed in order, so removal must come before addition
- The SEARCH section must match existing code exactly, including whitespace and comments
- SEARCH/REPLACE blocks replace ALL matching occurrences, so make them unique!

- Do **not** include explanations or comments outside the specified blocks.

**Example:**
**Examples:**

Input (with line numbers):
1. Simple Fix:
<original_code_with_line_numbers>
${addLineNumbers(`calculateCircleArea(r) = {
Math.PI * r * r;
}
myFn(x) = {
x * 2;
}
${addLineNumbers(`@name("💰 Expected Cost ($)")
@format("$.2s")
flightCost = normal({ mean: 600, stdev: 100 })
`)}
</original_code_with_line_numbers>

Output:

<explanation>
Fixed exponentiation and function naming for clarity
Updated normal distribution parameters for better accuracy
</explanation>
<edit>
<<<<<<< SEARCH
flightCost = normal({ mean: 600, stdev: 100 })
=======
flightCost = normal({ mean: 650, stdev: 120 })
>>>>>>> REPLACE
</edit>

2. Moving Code (with two blocks):
<original_code_with_line_numbers>
${addLineNumbers(`@name("💰 Expected Cost ($)")
@format("$.2s")
flightCost = normal({ mean: 600, stdev: 100 })
@name("🥇 Expected Benefit ($)")
@format("$.2s")
benefitEstimate = normal({ mean: 1500, stdev: 300 })

import "hub:ozziegooen/sTest" as sTest`)}
</original_code_with_line_numbers>

<explanation>
Move import statement to top of file
</explanation>
<edit>
<<<<<<< SEARCH
Math.PI * r * r
import "hub:ozziegooen/sTest" as sTest
=======
Math.PI * r ** 2
>>>>>>> REPLACE

<<<<<<< SEARCH
myFn(x) = {
@name("💰 Expected Cost ($)")
=======
myFunction(x) = {
import "hub:ozziegooen/sTest" as sTest

@name("💰 Expected Cost ($)")
>>>>>>> REPLACE
</edit>

Key Points for Code Movement:
- First block removes code from original location
- Second block adds code to new location
- Each block includes enough context to be unique
- Order matters: remove first, then add
`;
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export const ValueWithContextViewer: FC<Props> = ({
// root header is always hidden (unless forced, but we probably won't need it)
const headerVisibility = props.header ?? (isRoot ? "hide" : "normal");
const collapsible =
headerVisibility === "hide" ? false : (props.collapsible ?? true);
headerVisibility === "hide" ? false : props.collapsible ?? true;
const size = props.size ?? "normal";
const enableDropdownMenu = viewerType !== "tooltip";

Expand Down
16 changes: 3 additions & 13 deletions packages/hub/src/app/ai/AiDashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import clsx from "clsx";
import { FC, useRef, useState } from "react";
import { FC, useRef } from "react";

import { ClientWorkflow, ClientWorkflowResult } from "@quri/squiggle-ai";

Expand All @@ -26,19 +26,12 @@ export const AiDashboard: FC<Props> = ({
const { workflows, submitWorkflow, selectedWorkflow, selectWorkflow } =
useSquiggleWorkflows(initialWorkflows);

const [collapsedSidebar, setCollapsedSidebar] = useState(false);

const sidebarRef = useRef<{ edit: (code: string) => void }>(null);

const handleEditVersion = (code: string) => {
setCollapsedSidebar(false);
sidebarRef.current?.edit(code);
};

return (
<div className="flex">
{/* Left column: Mode Toggle, Chat, Form, and list of Workflows */}
<div className={clsx("w-1/5 p-2", collapsedSidebar && "hidden")}>
<div className={clsx("w-1/5 p-2")}>
<Sidebar
submitWorkflow={submitWorkflow}
selectWorkflow={selectWorkflow}
Expand All @@ -50,13 +43,10 @@ export const AiDashboard: FC<Props> = ({
</div>
{/* Right column: Menu and SquigglePlayground */}
{selectedWorkflow && (
<div className="flex-1">
<div className="min-w-0 flex-1 overflow-x-auto">
<WorkflowViewer
key={selectedWorkflow.id}
workflow={selectedWorkflow}
onFix={handleEditVersion}
expanded={collapsedSidebar}
setExpanded={setCollapsedSidebar}
/>
</div>
)}
Expand Down
11 changes: 3 additions & 8 deletions packages/hub/src/app/ai/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Outputs:

useEffect(() => {
if (workflows.length > prevWorkflowsLengthRef.current) {
selectWorkflow(workflows[workflows.length - 1].id);
selectWorkflow(workflows[0].id);
prevWorkflowsLengthRef.current = workflows.length;
}
}, [workflows, selectWorkflow]);
Expand Down Expand Up @@ -120,7 +120,7 @@ Outputs:
selectedIndex={mode === "edit" ? 1 : 0}
onChange={(index) => setMode(index === 0 ? "create" : "edit")}
>
<StyledTab.List stretch theme="primary">
<StyledTab.List stretch>
<StyledTab name="Create" />
<StyledTab name="Fix" />
</StyledTab.List>
Expand Down Expand Up @@ -156,12 +156,7 @@ Outputs:
).map((model) => model.id)}
required
/>
<Button
theme="primary"
wide
onClick={handleSubmit}
disabled={isSubmitDisabled}
>
<Button wide onClick={handleSubmit} disabled={isSubmitDisabled}>
Start Workflow
</Button>
<div className="flex-grow overflow-y-auto">
Expand Down
6 changes: 3 additions & 3 deletions packages/hub/src/app/ai/StepStatusIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { CheckCircleIcon, ErrorIcon, RefreshIcon } from "@quri/ui";
export const StepStatusIcon: FC<{ step: ClientStep }> = ({ step }) => {
switch (step.state) {
case "PENDING":
return <RefreshIcon className="animate-spin text-gray-400" />;
return <RefreshIcon className="animate-spin text-gray-400" size={16} />;
case "DONE":
return <CheckCircleIcon className="text-gray-400" />;
return <CheckCircleIcon className="text-gray-400" size={16} />;
case "FAILED":
return <ErrorIcon className="text-red-500" />;
return <ErrorIcon className="text-red-300" size={16} />;
default:
return null;
}
Expand Down
8 changes: 4 additions & 4 deletions packages/hub/src/app/ai/WorkflowStatusIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ export const WorkflowStatusIcon: FC<{ workflow: ClientWorkflow }> = ({
}) => {
switch (workflow.status) {
case "loading":
return <RefreshIcon className="animate-spin text-gray-500" />;
return <RefreshIcon className="animate-spin text-gray-500" size={16} />;
case "finished":
return workflow.result.isValid ? (
<CheckCircleIcon className="text-emerald-500" />
<CheckCircleIcon className="text-emerald-400" size={16} />
) : (
<ErrorIcon className="text-red-500" />
<ErrorIcon className="text-red-400" size={16} />
);
case "error":
return <ErrorIcon className="text-red-500" />;
return <ErrorIcon className="text-red-400" size={16} />;
}
};
4 changes: 2 additions & 2 deletions packages/hub/src/app/ai/WorkflowSummaryItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export const WorkflowSummaryItem: FC<{
return (
<div
className={clsx(
"rounded border p-2 text-sm",
isSelected ? "bg-gray-100" : "cursor-pointer"
"w-full border-b p-2 text-sm",
isSelected ? "bg-gray-100" : "cursor-pointer hover:bg-slate-50"
)}
onClick={isSelected ? undefined : onSelect}
>
Expand Down
2 changes: 1 addition & 1 deletion packages/hub/src/app/ai/WorkflowSummaryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const WorkflowSummaryList: FC<{
selectWorkflow: (id: string) => void;
}> = ({ workflows, selectedWorkflow, selectWorkflow }) => {
return (
<div className="flex max-h-[400px] flex-col space-y-2 overflow-y-auto pr-2">
<div className="flex max-h-[400px] w-full flex-col overflow-y-auto rounded-md border border-slate-200">
{workflows.map((workflow) => (
<WorkflowSummaryItem
key={workflow.id}
Expand Down
26 changes: 4 additions & 22 deletions packages/hub/src/app/ai/WorkflowViewer/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,22 @@
import { FC, ReactNode } from "react";

import { ClientWorkflow } from "@quri/squiggle-ai";
import { Button } from "@quri/ui";

import { WorkflowName } from "../WorkflowName";
import { WorkflowStatusIcon } from "../WorkflowStatusIcon";
import { ArtifactList } from "./ArtifactList";

// Common header for all workflow states
export const Header: FC<{
renderLeft: () => ReactNode;
renderRight: () => ReactNode;
workflow: ClientWorkflow;
expanded: boolean;
setExpanded: (expanded: boolean) => void;
}> = ({ renderLeft, renderRight, workflow, expanded, setExpanded }) => {
}> = ({ renderLeft, renderRight, workflow }) => {
return (
<div className="flex items-center justify-between rounded bg-gray-100 p-2">
<div className="flex items-center gap-2">
<div className="max-w-sm truncate text-sm font-medium">
<WorkflowName workflow={workflow} />
</div>
<WorkflowStatusIcon workflow={workflow} />
{renderLeft()}
<div className="flex items-center gap-3">
<ArtifactList artifacts={workflow.inputs} />
{renderLeft()}
</div>
<div className="flex gap-2">
{renderRight()}
{expanded ? (
<Button theme="primary" onClick={() => setExpanded(false)}>
Close
</Button>
) : (
<Button onClick={() => setExpanded(true)}>Full View</Button>
)}
</div>
<div className="flex gap-2">{renderRight()}</div>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ChevronLeftIcon, ChevronRightIcon, XIcon } from "@quri/ui";
import { useAvailableHeight } from "@/hooks/useAvailableHeight";

import { SquigglePlaygroundForWorkflow } from "../SquigglePlaygroundForWorkflow";
import { stepNames } from "../utils";
import { ArtifactList } from "./ArtifactList";
import { ArtifactMessages } from "./ArtifactMessages";

Expand Down Expand Up @@ -50,13 +51,13 @@ export const SelectedNodeSideView: FC<{

return (
<div
className="relative flex-1 overflow-y-auto border-l border-slate-200 bg-white p-4"
className="relative flex-1 overflow-y-auto bg-white p-4"
key={selectedNode.id}
ref={ref}
>
<div className="mb-4 flex items-center justify-between">
<h2 className="font-mono text-lg font-semibold text-slate-700">
{selectedNode.name}
<h2 className="text-lg font-semibold text-slate-600">
{stepNames[selectedNode.name] || selectedNode.name}
</h2>
<div className="flex items-center">
<NavButton
Expand Down Expand Up @@ -102,7 +103,7 @@ export const SelectedNodeSideView: FC<{
</div>
)}
{selectedNode.messages.length > 0 && (
<div className="flex w-1/4 min-w-[50px] flex-col">
<div className="flex w-1/4 min-w-[350px] flex-col">
<h3 className="mb-2 text-sm font-medium text-slate-500">
Messages:
</h3>
Expand Down
41 changes: 12 additions & 29 deletions packages/hub/src/app/ai/WorkflowViewer/StepNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FC, MouseEvent } from "react";
import { ClientStep } from "@quri/squiggle-ai";

import { StepStatusIcon } from "../StepStatusIcon";
import { ArtifactDisplay } from "./ArtifactDisplay";
import { stepNames } from "../utils";

type StepNodeProps = {
data: ClientStep;
Expand All @@ -15,10 +15,8 @@ type StepNodeProps = {

const getStepNodeClassName = (isSelected: boolean) =>
clsx(
"w-full cursor-pointer rounded-md border px-4 py-2 shadow-sm transition-colors",
isSelected
? "border-emerald-300 bg-emerald-100 hover:bg-emerald-200"
: "border-slate-200 bg-slate-100 hover:bg-slate-300"
"w-full cursor-pointer px-4 py-1.5 transition-colors",
isSelected ? "bg-slate-200" : "bg-slate-50 hover:bg-slate-100"
);

export const StepNode: FC<StepNodeProps> = ({
Expand All @@ -29,31 +27,16 @@ export const StepNode: FC<StepNodeProps> = ({
}) => {
return (
<div className={getStepNodeClassName(isSelected)} onClick={onClick}>
<div className="flex flex-col">
<div className="mb-1 flex items-center justify-between">
<div className="flex items-center gap-1">
{data.state !== "DONE" && (
<div className="shrink-0">
<StepStatusIcon step={data} />
</div>
)}
<div className="font-mono text-sm font-semibold text-slate-600">
<span className="mr-1 text-slate-400">{stepNumber}.</span>
{data.name}
</div>
</div>
<div className="flex items-center gap-2">
{Object.entries(data.outputs).map(([key, value]) => (
<ArtifactDisplay
key={key}
name={key}
artifact={value}
size={12}
showArtifactName={false}
/>
))}
</div>
<div className="flex items-center justify-between gap-1">
<div className="text-sm font-medium text-slate-600">
<span className="mr-1 text-slate-400">{stepNumber}.</span>
{stepNames[data.name] || data.name}
</div>
{data.state !== "DONE" && (
<div className="shrink-0">
<StepStatusIcon step={data} />
</div>
)}
</div>
</div>
);
Expand Down
Loading