better-chatbot / src /lib /ai /workflow /condition.ts
Bot
Initial commit for HF Spaces
05c5ed5
Raw
History Blame Contribute Delete
5.76 kB
import { safe } from "ts-safe";
import { OutputSchemaSourceKey } from "./workflow.interface";
/**
* Condition operators for string-based comparisons.
* Used to evaluate string values from node outputs.
*/
export enum StringConditionOperator {
Equals = "equals",
NotEquals = "not_equals",
Contains = "contains",
NotContains = "not_contains",
StartsWith = "starts_with",
EndsWith = "ends_with",
IsEmpty = "is_empty",
IsNotEmpty = "is_not_empty",
}
/**
* Condition operators for number-based comparisons.
* Inherits string equality operators and adds numeric comparisons.
*/
export enum NumberConditionOperator {
Equals = StringConditionOperator.Equals,
NotEquals = StringConditionOperator.NotEquals,
GreaterThan = "greater_than",
LessThan = "less_than",
GreaterThanOrEqual = "greater_than_or_equal",
LessThanOrEqual = "less_than_or_equal",
}
/**
* Condition operators for boolean value testing.
*/
export enum BooleanConditionOperator {
IsTrue = "is_true",
IsFalse = "is_false",
}
/**
* Gets the default condition operator for a given data type.
* Used when creating new conditions in the UI.
*/
export function getFirstConditionOperator(
type: "string" | "number" | "boolean",
) {
switch (type) {
case "string":
return StringConditionOperator.Equals;
case "number":
return NumberConditionOperator.Equals;
case "boolean":
return BooleanConditionOperator.IsTrue;
default:
return StringConditionOperator.Equals;
}
}
/**
* Union type of all possible condition operators.
*/
export type ConditionOperator =
| StringConditionOperator
| NumberConditionOperator
| BooleanConditionOperator;
/**
* A single condition rule that compares a value from a node output
* with a target value using a specified operator.
*/
export type ConditionRule = {
source: OutputSchemaSourceKey; // Reference to another node's output field
operator: ConditionOperator;
value?: string | number | boolean; // Comparison value (not needed for is_empty, is_not_empty, is_true, is_false)
};
/**
* A condition branch for if-elseIf-else structure.
* Each branch can have multiple conditions combined with AND/OR logic.
*/
export type ConditionBranch = {
id: "if" | "else" | (string & {});
type: "if" | "elseIf" | "else";
conditions: ConditionRule[]; // Not needed for 'else' type
logicalOperator: "AND" | "OR"; // How to combine multiple conditions, not needed for 'else'
};
/**
* Complete condition structure supporting if-elseIf-else branching.
* Used by Condition nodes to determine execution flow.
*/
export type ConditionBranches = {
if: ConditionBranch;
elseIf?: ConditionBranch[]; // Optional multiple elseIf branches
else: ConditionBranch; // Optional else branch
};
/**
* Evaluates a condition branch to determine if it should be executed.
*
* @param branch - The condition branch to evaluate
* @param getSourceValue - Function to get values from node outputs
* @returns True if the branch conditions are met
*/
export function checkConditionBranch(
branch: ConditionBranch,
getSourceValue: (
source: OutputSchemaSourceKey,
) => string | number | boolean | undefined,
): boolean {
// Evaluate all conditions in the branch
const results = branch.conditions?.map((condition) => {
return checkConditionRule({
operator: condition.operator,
target: String(condition.value || ""),
source: getSourceValue(condition.source),
});
}) ?? [false];
// Combine results based on logical operator
if (branch.logicalOperator === "AND") {
return results.every((result) => result);
}
return results.some((result) => result);
}
/**
* Evaluates a single condition rule.
*
* @param params - The condition rule parameters
* @returns True if the condition is met
*/
function checkConditionRule({
operator,
target,
source,
}: {
operator: ConditionOperator;
target: string;
source?: string | number | boolean;
}): boolean {
return safe(() => {
switch (operator) {
case StringConditionOperator.Equals:
if (source == target) return true;
break;
case StringConditionOperator.NotEquals:
if (source != target) return true;
break;
case StringConditionOperator.Contains:
if (String(source).includes(String(target))) return true;
break;
case StringConditionOperator.NotContains:
if (!String(source).includes(String(target))) return true;
break;
case StringConditionOperator.StartsWith:
if (String(source).startsWith(String(target))) return true;
break;
case StringConditionOperator.EndsWith:
if (String(source).endsWith(String(target))) return true;
break;
case StringConditionOperator.IsEmpty:
if (!source) return true;
break;
case StringConditionOperator.IsNotEmpty:
if (source) return true;
break;
case NumberConditionOperator.GreaterThan:
if (Number(source) > Number(target)) return true;
break;
case NumberConditionOperator.LessThan:
if (Number(source) < Number(target)) return true;
break;
case NumberConditionOperator.GreaterThanOrEqual:
if (Number(source) >= Number(target)) return true;
break;
case NumberConditionOperator.LessThanOrEqual:
if (Number(source) <= Number(target)) return true;
break;
case BooleanConditionOperator.IsTrue:
if (source) return true;
break;
case BooleanConditionOperator.IsFalse:
if (!source) return true;
break;
}
return false;
})
.ifFail((e) => {
console.error("Condition evaluation error:", e);
return false;
})
.unwrap();
}