UNPKG

@langchain/core

Version:
1 lines 14.7 kB
{"version":3,"file":"graph.cjs","names":["id: string | undefined","data: RunnableInterface | RunnableIOSchema","isRunnableInterface","node: Node","toJsonSchema","params?: { nodes: Record<string, Node>; edges: Edge[] }","stableNodeIds: Record<string, string | number>","item: Record<string, unknown>","id?: string","metadata?: Record<string, any>","source: Node","target: Node","data?: string","conditional?: boolean","edge: Edge","graph: Graph","isUuid","id: string","nodeLabels: Record<string, string>","nodeId: string","params?: {\n withStyles?: boolean;\n curveStyle?: string;\n nodeColors?: Record<string, string>;\n wrapLabelNWords?: number;\n }","drawMermaid","params?: {\n withStyles?: boolean;\n curveStyle?: string;\n nodeColors?: Record<string, string>;\n wrapLabelNWords?: number;\n backgroundColor?: string;\n }","drawMermaidImage","exclude: string[]","found: Node[]"],"sources":["../../src/runnables/graph.ts"],"sourcesContent":["import { v4 as uuidv4, validate as isUuid } from \"uuid\";\nimport type {\n RunnableInterface,\n RunnableIOSchema,\n Node,\n Edge,\n} from \"./types.js\";\nimport { isRunnableInterface } from \"./utils.js\";\nimport { drawMermaid, drawMermaidImage } from \"./graph_mermaid.js\";\nimport { toJsonSchema } from \"../utils/json_schema.js\";\n\nexport { Node, Edge };\n\nfunction nodeDataStr(\n id: string | undefined,\n data: RunnableInterface | RunnableIOSchema\n): string {\n if (id !== undefined && !isUuid(id)) {\n return id;\n } else if (isRunnableInterface(data)) {\n try {\n let dataStr = data.getName();\n dataStr = dataStr.startsWith(\"Runnable\")\n ? dataStr.slice(\"Runnable\".length)\n : dataStr;\n return dataStr;\n } catch {\n return data.getName();\n }\n } else {\n return data.name ?? \"UnknownSchema\";\n }\n}\n\nfunction nodeDataJson(node: Node) {\n // if node.data implements Runnable\n if (isRunnableInterface(node.data)) {\n return {\n type: \"runnable\",\n data: {\n id: node.data.lc_id,\n name: node.data.getName(),\n },\n };\n } else {\n return {\n type: \"schema\",\n data: { ...toJsonSchema(node.data.schema), title: node.data.name },\n };\n }\n}\n\nexport class Graph {\n nodes: Record<string, Node> = {};\n\n edges: Edge[] = [];\n\n constructor(params?: { nodes: Record<string, Node>; edges: Edge[] }) {\n this.nodes = params?.nodes ?? this.nodes;\n this.edges = params?.edges ?? this.edges;\n }\n\n // Convert the graph to a JSON-serializable format.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n toJSON(): Record<string, any> {\n const stableNodeIds: Record<string, string | number> = {};\n Object.values(this.nodes).forEach((node, i) => {\n stableNodeIds[node.id] = isUuid(node.id) ? i : node.id;\n });\n\n return {\n nodes: Object.values(this.nodes).map((node) => ({\n id: stableNodeIds[node.id],\n ...nodeDataJson(node),\n })),\n edges: this.edges.map((edge) => {\n const item: Record<string, unknown> = {\n source: stableNodeIds[edge.source],\n target: stableNodeIds[edge.target],\n };\n\n if (typeof edge.data !== \"undefined\") {\n item.data = edge.data;\n }\n\n if (typeof edge.conditional !== \"undefined\") {\n item.conditional = edge.conditional;\n }\n return item;\n }),\n };\n }\n\n addNode(\n data: RunnableInterface | RunnableIOSchema,\n id?: string,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n metadata?: Record<string, any>\n ): Node {\n if (id !== undefined && this.nodes[id] !== undefined) {\n throw new Error(`Node with id ${id} already exists`);\n }\n const nodeId = id ?? uuidv4();\n const node: Node = {\n id: nodeId,\n data,\n name: nodeDataStr(id, data),\n metadata,\n };\n this.nodes[nodeId] = node;\n return node;\n }\n\n removeNode(node: Node): void {\n // Remove the node from the nodes map\n delete this.nodes[node.id];\n\n // Filter out edges connected to the node\n this.edges = this.edges.filter(\n (edge) => edge.source !== node.id && edge.target !== node.id\n );\n }\n\n addEdge(\n source: Node,\n target: Node,\n data?: string,\n conditional?: boolean\n ): Edge {\n if (this.nodes[source.id] === undefined) {\n throw new Error(`Source node ${source.id} not in graph`);\n }\n if (this.nodes[target.id] === undefined) {\n throw new Error(`Target node ${target.id} not in graph`);\n }\n const edge: Edge = {\n source: source.id,\n target: target.id,\n data,\n conditional,\n };\n this.edges.push(edge);\n return edge;\n }\n\n firstNode(): Node | undefined {\n return _firstNode(this);\n }\n\n lastNode(): Node | undefined {\n return _lastNode(this);\n }\n\n /**\n * Add all nodes and edges from another graph.\n * Note this doesn't check for duplicates, nor does it connect the graphs.\n */\n extend(graph: Graph, prefix = \"\") {\n let finalPrefix = prefix;\n const nodeIds = Object.values(graph.nodes).map((node) => node.id);\n if (nodeIds.every(isUuid)) {\n finalPrefix = \"\";\n }\n\n const prefixed = (id: string) => {\n return finalPrefix ? `${finalPrefix}:${id}` : id;\n };\n\n Object.entries(graph.nodes).forEach(([key, value]) => {\n this.nodes[prefixed(key)] = { ...value, id: prefixed(key) };\n });\n\n const newEdges = graph.edges.map((edge) => {\n return {\n ...edge,\n source: prefixed(edge.source),\n target: prefixed(edge.target),\n };\n });\n // Add all edges from the other graph\n this.edges = [...this.edges, ...newEdges];\n const first = graph.firstNode();\n const last = graph.lastNode();\n return [\n first ? { id: prefixed(first.id), data: first.data } : undefined,\n last ? { id: prefixed(last.id), data: last.data } : undefined,\n ];\n }\n\n trimFirstNode(): void {\n const firstNode = this.firstNode();\n if (firstNode && _firstNode(this, [firstNode.id])) {\n this.removeNode(firstNode);\n }\n }\n\n trimLastNode(): void {\n const lastNode = this.lastNode();\n if (lastNode && _lastNode(this, [lastNode.id])) {\n this.removeNode(lastNode);\n }\n }\n\n /**\n * Return a new graph with all nodes re-identified,\n * using their unique, readable names where possible.\n */\n reid(): Graph {\n const nodeLabels: Record<string, string> = Object.fromEntries(\n Object.values(this.nodes).map((node) => [node.id, node.name])\n );\n const nodeLabelCounts = new Map<string, number>();\n Object.values(nodeLabels).forEach((label) => {\n nodeLabelCounts.set(label, (nodeLabelCounts.get(label) || 0) + 1);\n });\n\n const getNodeId = (nodeId: string): string => {\n const label = nodeLabels[nodeId];\n if (isUuid(nodeId) && nodeLabelCounts.get(label) === 1) {\n return label;\n } else {\n return nodeId;\n }\n };\n\n return new Graph({\n nodes: Object.fromEntries(\n Object.entries(this.nodes).map(([id, node]) => [\n getNodeId(id),\n { ...node, id: getNodeId(id) },\n ])\n ),\n edges: this.edges.map((edge) => ({\n ...edge,\n source: getNodeId(edge.source),\n target: getNodeId(edge.target),\n })),\n });\n }\n\n drawMermaid(params?: {\n withStyles?: boolean;\n curveStyle?: string;\n nodeColors?: Record<string, string>;\n wrapLabelNWords?: number;\n }): string {\n const {\n withStyles,\n curveStyle,\n nodeColors = {\n default: \"fill:#f2f0ff,line-height:1.2\",\n first: \"fill-opacity:0\",\n last: \"fill:#bfb6fc\",\n },\n wrapLabelNWords,\n } = params ?? {};\n const graph = this.reid();\n const firstNode = graph.firstNode();\n\n const lastNode = graph.lastNode();\n\n return drawMermaid(graph.nodes, graph.edges, {\n firstNode: firstNode?.id,\n lastNode: lastNode?.id,\n withStyles,\n curveStyle,\n nodeColors,\n wrapLabelNWords,\n });\n }\n\n async drawMermaidPng(params?: {\n withStyles?: boolean;\n curveStyle?: string;\n nodeColors?: Record<string, string>;\n wrapLabelNWords?: number;\n backgroundColor?: string;\n }): Promise<Blob> {\n const mermaidSyntax = this.drawMermaid(params);\n return drawMermaidImage(mermaidSyntax, {\n backgroundColor: params?.backgroundColor,\n });\n }\n}\n/**\n * Find the single node that is not a target of any edge.\n * Exclude nodes/sources with ids in the exclude list.\n * If there is no such node, or there are multiple, return undefined.\n * When drawing the graph, this node would be the origin.\n */\nfunction _firstNode(graph: Graph, exclude: string[] = []): Node | undefined {\n const targets = new Set(\n graph.edges\n .filter((edge) => !exclude.includes(edge.source))\n .map((edge) => edge.target)\n );\n\n const found: Node[] = [];\n for (const node of Object.values(graph.nodes)) {\n if (!exclude.includes(node.id) && !targets.has(node.id)) {\n found.push(node);\n }\n }\n return found.length === 1 ? found[0] : undefined;\n}\n\n/**\n * Find the single node that is not a source of any edge.\n * Exclude nodes/targets with ids in the exclude list.\n * If there is no such node, or there are multiple, return undefined.\n * When drawing the graph, this node would be the destination.\n */\nfunction _lastNode(graph: Graph, exclude: string[] = []): Node | undefined {\n const sources = new Set(\n graph.edges\n .filter((edge) => !exclude.includes(edge.target))\n .map((edge) => edge.source)\n );\n\n const found: Node[] = [];\n for (const node of Object.values(graph.nodes)) {\n if (!exclude.includes(node.id) && !sources.has(node.id)) {\n found.push(node);\n }\n }\n return found.length === 1 ? found[0] : undefined;\n}\n"],"mappings":"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"}