Skip to content

[WIP/DNM] crux extraction is optional from UI #130

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

Closed
wants to merge 4 commits into from
Closed
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
17 changes: 17 additions & 0 deletions common/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,23 @@ And now, here are the claims:
\${claims}
`;

export const defaultCruxPrompt = `
I'm going to give you a topic with a description and a list of high-level claims about this topic made by different participants,
identified by pseudonyms like "Person 1" or "A". I want you to formulate a new, specific statement called a "cruxClaim"
which would best split the participants into two groups, based on all their
statements on this topic: one group which would agree with the statement, and one which would disagree.
Please explain your reasoning and assign participants into "agree" and "disagree" groups.
return a JSON object of the form
{
"crux" : {
"cruxClaim" : string // the new extracted claim
"agree" : list of strings // list of the given participants who would agree with the cruxClaim
"disagree" : list strings // list of the given participants who would disagree with the cruxClaim
"explanation" : string // reasoning for why you synthesized this cruxClaim from the participants' perspective
}
}
`;

/**
* Takes a prompt and data, and then inserts those values into the prompt.
* The reason for this is that the string the user provides can't actually be a template literal.
Expand Down
2 changes: 2 additions & 0 deletions common/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const llmUserConfig = z.object({
extractionInstructions: z.string().min(1),
dedupInstructions: z.string().min(1),
cruxInstructions: z.string().optional(),
cruxesEnabled: z.boolean().optional(),
});

export type LLMUserConfig = z.infer<typeof llmUserConfig>;
Expand All @@ -96,6 +97,7 @@ export const oldOptions = z.object({
extractionInstructions: z.string(),
dedupInstructions: z.string(),
cruxInstructions: z.string().optional(),
cruxesEnabled: z.boolean().optional(),
batchSize: z.number(),
filename: z.string(),
googleSheet: z
Expand Down
3 changes: 3 additions & 0 deletions express-server/src/workers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const setupPipelineWorker = (connection: Redis) => {
extractionInstructions: "",
dedupInstructions: "",
cruxInstructions: "",
cruxesEnabled: false,
batchSize: 2, // lower to avoid rate limits! initial was 10,
};

Expand All @@ -83,6 +84,7 @@ const setupPipelineWorker = (connection: Redis) => {
});

const options: schema.OldOptions = { ...defaultConfig, ...config };
console.log("CRUX OPTIONS: ", options.cruxesEnabled);

const [
topicTreeLLMConfig,
Expand Down Expand Up @@ -164,6 +166,7 @@ const setupPipelineWorker = (connection: Redis) => {
});
logTokensInTracker(tracker_step2);

console.log("CRUX OPTIONS: ", options.cruxesEnabled);
console.log("Step 2.5: Optionally extract cruxes");
const {
cruxClaims,
Expand Down
47 changes: 47 additions & 0 deletions next-client/src/components/create/CreateReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { User } from "firebase/auth";
import { useFormStatus } from "react-dom";
import { useRouter } from "next/navigation";
import { signInWithGoogle } from "@/lib/firebase/auth";
import { crux } from "tttc-common/schema";

const fetchToken = async (
user: User | null,
Expand Down Expand Up @@ -85,6 +86,8 @@ const form = z.object({
clusteringInstructions: z.string().min(1),
extractionInstructions: z.string().min(1),
dedupInstructions: z.string().min(1),
cruxInstructions: z.string().optional(),
cruxesEnabled: z.boolean().optional(),
});

function Center({ children }: React.PropsWithChildren) {
Expand Down Expand Up @@ -179,6 +182,8 @@ function CreateReportComponent({ token }: { token: string | null }) {
clusteringInstructions: prompts.defaultClusteringPrompt,
extractionInstructions: prompts.defaultExtractionPrompt,
dedupInstructions: prompts.defaultDedupPrompt,
cruxInstructions: prompts.defaultCruxPrompt,
cruxesEnabled: false,
},
});

Expand All @@ -196,6 +201,7 @@ function CreateReportComponent({ token }: { token: string | null }) {
<FormHeader />
<FormDescription />
<FormDataInput files={files} setFiles={setFiles} />
<EnableResearchFeatures />
<CustomizePrompts />
<CostEstimate files={files} />
<div>
Expand Down Expand Up @@ -524,6 +530,42 @@ const FormOpenAIKey = () => {
);
};

const EnableResearchFeatures = () => {
const [areCruxesEnabled, setCruxesEnabled] = React.useState(false);
const handleChange = (event) => {
setCruxesEnabled((areCruxesEnabled) => !areCruxesEnabled);
console.log("ARE CRUXES ENABLED? : ", areCruxesEnabled.toString());
};
return (
<Col gap={4}>
<h4>Enable Research Features</h4>
<Col gap={2}>
<Col>
<label htmlFor="title" className="font-medium">
Extract likely crux statements
</label>
<p className="p2 text-muted-foreground">
As an extra processing step, suggest pairs of
perspective-summarizing statements which would best split the
respondents (into agree/disagree sides/groups of about equal size).
</p>
<div style={{ margin: "8px 0" }}>
<input
type="checkbox"
checked={areCruxesEnabled}
onChange={handleChange}
/>
<label style={{ paddingLeft: "8px" }}>
Suggest top crux pairs
<p>Are cruxes enabled? {areCruxesEnabled.toString()}</p>
</label>
</div>
</Col>
</Col>
</Col>
);
};

const CustomizePrompts = () => (
<Col gap={8}>
<Col gap={4}>
Expand Down Expand Up @@ -555,6 +597,11 @@ const CustomizePrompts = () => (
subheader="In the last step, AI collects very similar or near-duplicate statements under one representative claim"
inputName="dedupInstructions"
/>
<CustomizePromptSection
title="Optional – Suggest crux summary statements of opposing perspectives"
subheader="In this optional research step, AI suggests pairs of 'crux' statements which would best split participants into agree/disagree groups or sides of about equal size"
inputName="cruxInstructions"
/>
</Col>
);

Expand Down
18 changes: 2 additions & 16 deletions next-client/src/features/submission/actions/SubmitAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,6 @@ export default async function submitAction(
throw new Error("Missing data. Check your csv file");
}

const tempCruxInstructions = `I'm going to give you a topic with a description and a list of high-level claims about this topic made by different participants,
identified by pseudonyms like "Person 1" or "A". I want you to formulate a new, specific statement called a "cruxClaim"
which would best split the participants into two groups, based on all their
statements on this topic: one group which would agree with the statement, and one which would disagree.
Please explain your reasoning and assign participants into "agree" and "disagree" groups.
return a JSON object of the form
{
"crux" : {
"cruxClaim" : string // the new extracted claim
"agree" : list of strings // list of the given participants who would agree with the cruxClaim
"disagree" : list strings // list of the given participants who would disagree with the cruxClaim
"explanation" : string // reasoning for why you synthesized this cruxClaim from the participants' perspective
}
}
`;
const config: LLMUserConfig = llmUserConfig.parse({
apiKey: formData.get("apiKey"),
title: formData.get("title"),
Expand All @@ -58,7 +43,8 @@ export default async function submitAction(
systemInstructions: formData.get("systemInstructions"),
extractionInstructions: formData.get("extractionInstructions"),
dedupInstructions: formData.get("dedupInstructions"),
cruxInstructions: tempCruxInstructions,
cruxInstructions: formData.get("cruxInstructions"),
cruxesEnabled: formData.get("cruxesEnabled"),
});
const dataPayload: DataPayload = ["csv", data];

Expand Down