Repository: Significant-Gravitas/Auto-GPT
File: autogpt_platform/backend/backend/data/graph.py (Python)

Code before:
                continue
            else:
                result[key] = value
        return result


# --------------------- Model functions --------------------- #


async def get_node(node_id: str) -> Node:
    node = await AgentNode.prisma().find_unique_or_raise(
        where={"id": node_id},
        include=AGENT_NODE_INCLUDE,
    )
    return Node.from_db(node)


async def get_graphs(
    user_id: str,
    include_executions: bool = False,
    filter_by: Literal["all", "active", "template"] | None = "active",
) -> list[Graph]:
    """
    Retrieves graph metadata objects.
    Default behaviour is to get all currently active graphs.
    When filter_by="all", retrieves all versions of graphs.

    Args:
        include_executions: Whether to include executions in the graph metadata.
        filter_by: An optional filter to either select all versions,
            templates or active graphs. Defaults to "active".
        user_id: The ID of the user that owns the graph.

    Returns:
        list[Graph]: A list of objects representing the retrieved graph metadata.
    """
    where_clause: AgentGraphWhereInput = {"userId": user_id}

    if filter_by == "active":
        where_clause["isActive"] = True
    elif filter_by == "template":
        where_clause["isTemplate"] = True

    graph_include = AGENT_GRAPH_INCLUDE
    graph_include["AgentGraphExecution"] = include_executions

    # For "all" return all versions
    if filter_by == "all":
        graphs = await AgentGraph.prisma().find_many(
            where=where_clause,
            order={"version": "desc"},

Review comment (suggestion):
I'm pretty sure we have bajillions of graphs without an execution because of the way we save the graph. Should we have a filter to fetch all graphs with at least a single execution instead? Or even better, the fetch is done using `get_executions` (which fetches all graph executions for a user, I think we don't have it yet, but it should be easy to add) instead of `get_graph` and the graph we are fetching is still only the active ones.

Code after:

        return GraphModel(
            id=graph.id,
            user_id=graph.userId,
            version=graph.version,
            is_active=graph.isActive,
            is_template=graph.isTemplate,
            name=graph.name or "",
            description=graph.description or "",
            executions=executions,
            nodes=[
                GraphModel._process_node(node, hide_credentials)
                for node in graph.AgentNodes or []
            ],
            links=list(
                {
                    Link.from_db(link)
                    for node in graph.AgentNodes or []
                    for link in (node.Input or []) + (node.Output or [])
                }
            ),
        )

    @staticmethod
    def _process_node(node: AgentNode, hide_credentials: bool) -> NodeModel:
        node_dict = {field: getattr(node, field) for field in node.model_fields}
        if hide_credentials and "constantInput" in node_dict:
            constant_input = json.loads(
                node_dict["constantInput"], target_type=dict[str, Any]
            )
            constant_input = GraphModel._hide_credentials_in_input(constant_input)
            node_dict["constantInput"] = json.dumps(constant_input)
        return NodeModel.from_db(AgentNode(**node_dict))

    @staticmethod
    def _hide_credentials_in_input(input_data: dict[str, Any]) -> dict[str, Any]:
        sensitive_keys = ["credentials", "api_key", "password", "token", "secret"]
        result = {}
        for key, value in input_data.items():
            if isinstance(value, dict):
                result[key] = GraphModel._hide_credentials_in_input(value)
            elif isinstance(value, str) and any(
                sensitive_key in key.lower() for sensitive_key in sensitive_keys
            ):
                # Skip this key-value pair in the result
                continue
            else:
                result[key] = value
        return result