{showWarning && (
You may want to add more fields for better suggestions.
)}
+
+ {didGenerateColumnError && (
+
+ Failed to generate column. Please try again.
+
+ )}
+
);
diff --git a/chainforge/react-server/src/TabularDataNode.tsx b/chainforge/react-server/src/TabularDataNode.tsx
index 0083ad6..cbdef57 100644
--- a/chainforge/react-server/src/TabularDataNode.tsx
+++ b/chainforge/react-server/src/TabularDataNode.tsx
@@ -480,21 +480,53 @@ const TabularDataNode: React.FC = ({ data, id }) => {
tableData.map((row) => row.value || ""),
);
- // This function will add the new columns to the right of the existing columns
- const addColumns = (newColumns: TabularDataColType[]) => {
- const updatedColumns = [...tableColumns, ...newColumns];
+ // Function to add new columns to the right of the existing columns (with optional row values)
+ const addColumns = (
+ newColumns: TabularDataColType[],
+ rowValues?: string[] // If values are passed, they will be used to populate the new columns
+ ) => {
+ setTableColumns((prevColumns) => {
+ const updatedColumns = [...prevColumns, ...newColumns];
- // Add blank values for the new column in each row
- const updatedRows = tableData.map((row) => {
- const updatedRow = { ...row };
- newColumns.forEach((col) => {
- updatedRow[col.key] = ""; // Default value for new column
+ setTableData((prevData) => {
+ let updatedRows: TabularDataRowType[] = [];
+
+ if (prevData.length > 0) {
+ // Update the existing rows with the new column values
+ updatedRows = prevData.map((row, rowIndex) => {
+ const updatedRow = { ...row };
+ newColumns.forEach((col) => {
+ // If rowValues, use them for the new column
+ updatedRow[col.key] =
+ rowValues && rowValues[rowIndex] !== undefined
+ ? rowValues[rowIndex]
+ : ""; // Default to empty value
+ });
+ return updatedRow;
+ });
+ } else if (rowValues) {
+ // If no rows exist but rowValues are passed, create new rows
+ updatedRows = rowValues.map((value) => {
+ const newRow: TabularDataRowType = { __uid: uuidv4() };
+ newColumns.forEach((col) => {
+ newRow[col.key] = value || "";
+ });
+ return newRow;
+ });
+ } else {
+ // Create a single blank row
+ const blankRow: TabularDataRowType = { __uid: uuidv4() };
+ newColumns.forEach((col) => {
+ blankRow[col.key] = "";
+ });
+ updatedRows.push(blankRow);
+ }
+
+ return updatedRows; // Update table rows
});
- return updatedRow;
- });
- setTableColumns(updatedColumns);
- setTableData(updatedRows);
+ return updatedColumns; // Update table columns
+ });
};
// Function to add multiple rows to the table
diff --git a/chainforge/react-server/src/backend/ai.ts b/chainforge/react-server/src/backend/ai.ts
index 1290dae..0bdf6d4 100644
--- a/chainforge/react-server/src/backend/ai.ts
+++ b/chainforge/react-server/src/backend/ai.ts
@@ -114,6 +114,7 @@ function autofillSystemMessage(
/**
* Generate the system message used for autofillingTables.
* @param n number of rows to generate
+ * @param templateVariables list of template variables to use
*/
function autofillTableSystemMessage(
n: number,
@@ -122,6 +123,18 @@ function autofillTableSystemMessage(
return `Here is a table. Generate ${n} more commands or items following the pattern. You must format your response as a markdown table with labeled columns and a divider with only the next ${n} generated commands or items of the table. ${templateVariables && templateVariables.length > 0 ? templateVariableMessage(templateVariables) : ""}`;
}
+/**
+ * Generate the system message used for generate column.
+ * @param templateVariables list of template variables to use
+ * @param prompt description or pattern for the column content
+ */
+function generateColumnSystemMessage(
+ templateVariables?: string[],
+ prompt?: string,
+): string {
+ return `Here is a table. Generate exactly one column with an appropriate header given the prompt: ${prompt} and one divider with the appropriate commands or items and the same amount of rows. Output the formatted labled markdown column, with each command or item as a new row. ${templateVariables && templateVariables.length > 0 ? templateVariableMessage(templateVariables) : ""}`;
+}
+
/**
* Generate the system message used for generate and replace (GAR).
*/
@@ -386,7 +399,7 @@ export async function autofillTable(
// Return the updated table with "n" number of rows
return {
cols: input.cols,
- rows: newRows // Return the new rows generated by the LLM
+ rows: newRows, // Return the new rows generated by the LLM
};
} catch (error) {
console.error("Error in autofillTable:", error);
@@ -396,6 +409,82 @@ export async function autofillTable(
}
}
+/**
+ * Uses an LLM to generate one new column with data based on the pattern explained in `prompt`.
+ * @param prompt Description or pattern for the column content.
+ * @param provider The LLM provider to use (e.g., OpenAI, Bedrock).
+ * @param apiKeys API keys required for the LLM query.
+ * @returns A promise resolving to an array of strings (column values).
+ */
+export async function generateColumn(
+ tableData: { cols: string[]; rows: Row[] },
+ prompt: string,
+ provider: string,
+ apiKeys: Dict,
+): Promise<{col: string; rows: string[]}> {
+
+ // Build a unique ID based on the input prompt
+ const id = JSON.stringify([prompt]);
+
+ // Encode the input table to a markdown table
+ const encoded = encodeTable(tableData.cols, tableData.rows);
+
+ // Extract template variables from the columns and rows
+ const templateVariables = [
+ ...new Set([
+ ...new StringTemplate(tableData.rows.join("\n")).get_vars(),
+ ...new StringTemplate(tableData.cols.join("\n")).get_vars(),
+ ]),
+ ];
+
+ // History for the system message to set up LLM behavior
+ const history: ChatHistoryInfo[] = [
+ {
+ messages: [
+ {
+ role: "system",
+ content: generateColumnSystemMessage(templateVariables, prompt),
+ },
+ ],
+ fill_history: {},
+ },
+ ];
+
+ try {
+ // Query the LLM
+ const result = await queryLLM(
+ id,
+ getAIFeaturesModels(provider).small, // Use the small model
+ 1,
+ encoded,
+ {},
+ history,
+ apiKeys,
+ true,
+ );
+
+ // Handle any errors in the response
+ if (result.errors && Object.keys(result.errors).length > 0) {
+ throw new Error(Object.values(result.errors)[0].toString());
+ }
+
+ console.log("LLM said: ", result.responses[0].responses[0]);
+
+ // Decode the LLM response as a column in a markdown table
+ const columnValues = decodeTable(result.responses[0].responses[0] as string);
+
+ return {
+ col:columnValues.cols[0],
+ rows: columnValues.rows
+ };
+ } catch (error) {
+ console.error("Error in generateColumn:", error);
+ throw new AIError(
+ `Failed to generate column. Details: ${(error as Error).message || error}`,
+ );
+ }
+}
+
/**
* Uses an LLM to generate `n` new rows based on the pattern explained in `prompt`.
* @param prompt