diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index 5c0e01f..0000000 --- a/src/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# src package initialization \ No newline at end of file diff --git a/src/__tests__/graph.test.js b/src/__tests__/graph.test.js deleted file mode 100644 index 9cca93c..0000000 --- a/src/__tests__/graph.test.js +++ /dev/null @@ -1,105 +0,0 @@ -const { Graph, Node, ReflectionNode, RewritingNode } = require('../index'); - -describe('Graph with Reflection and Rewriting Nodes', () => { - test('ReflectionNode creates reflected nodes with copied edges', () => { - const graph = new Graph(); - const a = new Node('A'); - const b = new Node('B'); - const c = new Node('C'); - graph.addNode(a); - graph.addNode(b); - graph.addNode(c); - graph.addEdge('A', 'B'); - graph.addEdge('B', 'C'); - - const r = new ReflectionNode('R'); - graph.addNode(r); - graph.addEdge('R', 'B'); - - r.reflect(graph); - - const bRef = graph.getNode('B_ref'); - expect(bRef).toBeDefined(); - expect(bRef.type).toBe('generic'); - const edges = graph.edges.get('B_ref'); - expect(edges).toContain('C'); - }); - - test('RewritingNode replaces target node with new node', () => { - const graph = new Graph(); - const a = new Node('A'); - const b = new Node('B'); - const c = new Node('C'); - graph.addNode(a); - graph.addNode(b); - graph.addNode(c); - graph.addEdge('A', 'B'); - graph.addEdge('B', 'C'); - - const w = new RewritingNode('W'); - graph.addNode(w); - graph.addEdge('W', 'C'); - - const d = new Node('D'); - w.rewrite(graph, 'C', d); - - expect(graph.getNode('C')).toBeUndefined(); - expect(graph.getNode('D')).toBeDefined(); - const edges = graph.edges.get('B'); - expect(edges).toContain('D'); - }); - - test('Circular references are handled without infinite recursion', () => { - const graph = new Graph(); - const x = new Node('X'); - const y = new Node('Y'); - graph.addNode(x); - graph.addNode(y); - graph.addEdge('X', 'Y'); - graph.addEdge('Y', 'X'); - - const r = new ReflectionNode('R'); - graph.addNode(r); - graph.addEdge('R', 'X'); - - expect(() => r.reflect(graph)).not.toThrow(); - - const xRef = graph.getNode('X_ref'); - expect(xRef).toBeDefined(); - const edges = graph.edges.get('X_ref'); - expect(edges).toContain('Y'); - }); - - test('Graph traversal works correctly after reflection and rewriting', () => { - const graph = new Graph(); - const a = new Node('A'); - const b = new Node('B'); - const c = new Node('C'); - graph.addNode(a); - graph.addNode(b); - graph.addNode(c); - graph.addEdge('A', 'B'); - graph.addEdge('B', 'C'); - - const r = new ReflectionNode('R'); - graph.addNode(r); - graph.addEdge('R', 'B'); - r.reflect(graph); - - const w = new RewritingNode('W'); - graph.addNode(w); - graph.addEdge('W', 'C'); - const d = new Node('D'); - w.rewrite(graph, 'C', d); - - const traversal = graph.traverse('A'); - // Should visit A, B, D, and B_ref (which points to D) - expect(traversal).toContain('A'); - expect(traversal).toContain('B'); - expect(traversal).toContain('D'); - expect(traversal).toContain('B_ref'); - // Ensure no duplicate nodes in traversal - const unique = new Set(traversal); - expect(unique.size).toBe(traversal.length); - }); -}); \ No newline at end of file diff --git a/src/agent.js b/src/agent.js deleted file mode 100644 index dae0765..0000000 --- a/src/agent.js +++ /dev/null @@ -1,17 +0,0 @@ -import { OpenAI } from 'langchain-openai'; - -/** - * Generates a response from the LLM for a given prompt. - * - * @param {string} prompt - The input prompt to send to the LLM. - * @returns {Promise} The LLM's response text. - */ -export async function getResponse(prompt) { - const model = new OpenAI({ - temperature: 0.7, - modelName: 'gpt-3.5-turbo' - }); - - const response = await model.invoke(prompt); - return response; -} \ No newline at end of file diff --git a/src/agent.py b/src/agent.py deleted file mode 100644 index 871bf98..0000000 --- a/src/agent.py +++ /dev/null @@ -1,82 +0,0 @@ -import os -from typing import Dict, List - -from langgraph.graph import StateGraph, END -from langchain_openai import ChatOpenAI -from langchain_core.messages import HumanMessage, AIMessage, BaseMessage - - -# Define the state type for the graph -class GraphState: - messages: List[BaseMessage] - - -def llm_node(state: Dict[str, List[BaseMessage]]) -> Dict[str, List[BaseMessage]]: - """ - Node that sends the current conversation to the LLM and appends the response. - """ - # Retrieve the current messages - messages = state["messages"] - - # Initialize the LLM (OpenAI) - llm = ChatOpenAI( - api_key=os.getenv("OPENAI_API_KEY"), - model="gpt-4o-mini", # You can change the model as needed - ) - - # Call the LLM with the conversation history - response: AIMessage = llm.invoke(messages) - - # Append the LLM response to the conversation - new_messages = messages + [response] - return {"messages": new_messages} - - -def create_agent() -> StateGraph: - """ - Creates a simple LangGraph agent that uses the LLM node. - """ - # Initialize the graph - graph = StateGraph(GraphState) - - # Add the LLM node - graph.add_node("llm", llm_node) - - # Set the entry point and end condition - graph.set_entry_point("llm") - graph.add_edge("llm", END) - - return graph - - -def run_agent(prompt: str) -> str: - """ - Runs the agent with the given prompt and returns the LLM's final response. - """ - # Create the graph - graph = create_agent() - - # Build the initial state - initial_state = {"messages": [HumanMessage(content=prompt)]} - - # Run the graph - final_state = graph.invoke(initial_state) - - # Extract the last AI message - ai_messages = [msg for msg in final_state["messages"] if isinstance(msg, AIMessage)] - if not ai_messages: - return "No response from LLM." - return ai_messages[-1].content - - -if __name__ == "__main__": - # Simple CLI usage - import argparse - - parser = argparse.ArgumentParser(description="Run the LangGraph agent with OpenAI LLM.") - parser.add_argument("prompt", type=str, help="The prompt to send to the agent.") - args = parser.parse_args() - - response = run_agent(args.prompt) - print("Agent response:") - print(response) \ No newline at end of file diff --git a/src/graph.js b/src/graph.js deleted file mode 100644 index c00e620..0000000 --- a/src/graph.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Simple graph implementation that executes nodes in a defined sequence. - */ -class Graph { - constructor() { - this.nodes = {}; - } - - /** - * Adds a node to the graph. - * @param {string} name - Unique name of the node. - * @param {function} fn - Function that processes input and returns output. - */ - addNode(name, fn) { - if (typeof fn !== 'function') { - throw new Error('Node must be a function.'); - } - this.nodes[name] = fn; - } - - /** - * Executes a sequence of nodes with the given input. - * @param {Array} nodeSequence - Ordered list of node names to execute. - * @param {any} input - Initial input for the first node. - * @returns {Promise} - Final output after all nodes have processed the data. - */ - async run(nodeSequence, input) { - if (!Array.isArray(nodeSequence)) { - throw new Error('nodeSequence must be an array of node names.'); - } - let data = input; - for (const name of nodeSequence) { - const fn = this.nodes[name]; - if (!fn) { - throw new Error(`Node "${name}" not found in the graph.`); - } - try { - data = await fn(data); - } catch (err) { - throw new Error(`Error in node "${name}": ${err.message}`); - } - } - return data; - } -} - -module.exports = Graph; \ No newline at end of file diff --git a/src/graph.py b/src/graph.py deleted file mode 100644 index 60a8906..0000000 --- a/src/graph.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -Graph implementation that connects nodes and executes them in sequence. -""" - -from typing import Dict, List - -from .nodes import BaseNode, InputNode, OutputNode, ReflectionNode, RewritingNode - - -class Graph: - """ - Simple directed acyclic graph for node execution. - """ - - def __init__(self): - self.nodes: Dict[str, BaseNode] = {} - self.edges: Dict[str, List[str]] = {} - - def add_node(self, node: BaseNode): - self.nodes[node.node_id] = node - self.edges.setdefault(node.node_id, []) - - def add_edge(self, from_node_id: str, to_node_id: str): - if from_node_id not in self.nodes or to_node_id not in self.nodes: - raise ValueError("Both nodes must be added before creating an edge.") - self.edges[from_node_id].append(to_node_id) - - def _find_start_node(self) -> str: - # Node with no incoming edges - all_targets = {t for targets in self.edges.values() for t in targets} - for node_id in self.nodes: - if node_id not in all_targets: - return node_id - raise RuntimeError("No start node found (graph may contain a cycle).") - - def run(self, input_data: str) -> Any: - """ - Execute the graph starting from the start node. - """ - current_node_id = self._find_start_node() - data = input_data - while True: - node = self.nodes[current_node_id] - data = node.process(data) - successors = self.edges.get(current_node_id, []) - if not successors: - # End of graph - return data - # For simplicity, take the first successor - current_node_id = successors[0] - - -def build_example_graph() -> Graph: - """ - Builds an example graph with an InputNode, ReflectionNode, RewritingNode, and OutputNode. - """ - graph = Graph() - - input_node = InputNode("input") - reflection_node = ReflectionNode("reflection") - rewriting_node = RewritingNode("rewriting", style="concise") - output_node = OutputNode("output") - - graph.add_node(input_node) - graph.add_node(reflection_node) - graph.add_node(rewriting_node) - graph.add_node(output_node) - - graph.add_edge("input", "reflection") - graph.add_edge("reflection", "rewriting") - graph.add_edge("rewriting", "output") - - return graph \ No newline at end of file diff --git a/src/graph.ts b/src/graph.ts deleted file mode 100644 index 8b43f10..0000000 --- a/src/graph.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { BaseNode } from './nodes/baseNode'; -import { ReflectionNode } from './nodes/reflectionNode'; -import { RewriteNode, RewriteFunction } from './nodes/rewriteNode'; - -export type Edge = { - from: string; - out: string; - to: string; - in: string; -}; - -export class Graph { - private nodes: Map; - private edges: Edge[]; - private nodeCounter: number; - - constructor() { - this.nodes = new Map(); - this.edges = []; - this.nodeCounter = 0; - } - - private generateId(): string { - return `node_${this.nodeCounter++}`; - } - - /** - * Creates a node of the specified type. - * @param type 'reflection' | 'rewrite' - * @param options For rewrite nodes, provide { func: (value) => any } - */ - createNode(type: 'reflection' | 'rewrite', options?: any): BaseNode { - const id = this.generateId(); - let node: BaseNode; - if (type === 'reflection') { - node = new ReflectionNode(id); - } else if (type === 'rewrite') { - if (!options || typeof options.func !== 'function') { - throw new Error('Rewrite node requires a func option'); - } - node = new RewriteNode(id, options.func); - } else { - throw new Error(`Unknown node type: ${type}`); - } - this.nodes.set(id, node); - return node; - } - - addNode(node: BaseNode): void { - if (this.nodes.has(node.id)) { - throw new Error(`Node with id ${node.id} already exists`); - } - this.nodes.set(node.id, node); - } - - addEdge(from: string, out: string, to: string, inKey: string): void { - if (!this.nodes.has(from) || !this.nodes.has(to)) { - throw new Error('Both nodes must exist to add an edge'); - } - this.edges.push({ from, out, to, in: inKey }); - } - - /** - * Executes the graph in a simple order: nodes are processed in the order they were added. - * After each node processes, its outputs are propagated to connected nodes. - */ - run(): void { - for (const node of this.nodes.values()) { - node.process(); - for (const edge of this.edges.filter(e => e.from === node.id)) { - const target = this.nodes.get(edge.to); - if (!target) continue; - const value = node.outputs.get(edge.out); - target.inputs.set(edge.in, value); - } - } - } - - getNode(id: string): BaseNode | undefined { - return this.nodes.get(id); - } -} \ No newline at end of file diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 145c522..0000000 --- a/src/index.js +++ /dev/null @@ -1,80 +0,0 @@ -class Graph { - constructor() { - this.nodes = new Map(); // nodeId -> nodeData - this.edges = new Map(); // nodeId -> Set of neighbor nodeIds - this.edgeData = new Map(); // key `${from}->${to}` -> data - } - - addNode(id, data = {}) { - if (this.nodes.has(id)) { - throw new Error(`Node with id ${id} already exists`); - } - this.nodes.set(id, data); - this.edges.set(id, new Set()); - } - - addEdge(from, to, data = {}) { - if (!this.nodes.has(from) || !this.nodes.has(to)) { - throw new Error(`Both nodes must exist to add an edge`); - } - this.edges.get(from).add(to); - const key = `${from}->${to}`; - this.edgeData.set(key, data); - } - - getNeighbors(id) { - if (!this.nodes.has(id)) { - throw new Error(`Node with id ${id} does not exist`); - } - return Array.from(this.edges.get(id)); - } - - getNode(id) { - return this.nodes.get(id); - } - - getAllNodes() { - return Array.from(this.nodes.keys()); - } - - getAllEdges() { - const edges = []; - for (const [from, neighbors] of this.edges.entries()) { - for (const to of neighbors) { - const key = `${from}->${to}`; - edges.push({ from, to, data: this.edgeData.get(key) }); - } - } - return edges; - } - - getEdgeData(from, to) { - const key = `${from}->${to}`; - return this.edgeData.get(key); - } - - // Reflection methods - getProperties() { - return Object.getOwnPropertyNames(this); - } - - getMethods() { - const proto = Object.getPrototypeOf(this); - return Object.getOwnPropertyNames(proto).filter( - (name) => typeof this[name] === 'function' && name !== 'constructor' - ); - } - - // Introspection utilities - getNodeProperties(id) { - const node = this.nodes.get(id); - return node ? Object.keys(node) : null; - } - - getEdgeProperties(from, to) { - const data = this.getEdgeData(from, to); - return data ? Object.keys(data) : null; - } -} - -module.exports = Graph; \ No newline at end of file diff --git a/src/index.py b/src/index.py deleted file mode 100644 index 60e9ec1..0000000 --- a/src/index.py +++ /dev/null @@ -1,138 +0,0 @@ -""" -Graph data structure with reflection and introspection capabilities. - -This Python implementation mirrors the JavaScript version found in -`src/index.js`. It provides: - -* Node and edge management (add, retrieve, list) -* Directed edges with optional data -* Reflection utilities (`get_properties`, `get_methods`) -* Introspection utilities (`get_node_properties`, `get_edge_properties`) - -The API is intentionally similar to the JS version so that tests written in -JavaScript can be easily ported to Python if needed. -""" - -from __future__ import annotations - -from typing import Any, Dict, Iterable, List, Set, Tuple, Union - - -class Graph: - """ - Directed graph with optional data on nodes and edges. - """ - - def __init__(self) -> None: - # node_id -> node_data (dict) - self.nodes: Dict[Any, Dict[str, Any]] = {} - # node_id -> set of neighbor node_ids - self.edges: Dict[Any, Set[Any]] = {} - # (from, to) -> edge_data (dict) - self.edge_data: Dict[Tuple[Any, Any], Dict[str, Any]] = {} - - # ------------------------------------------------------------------ - # Core graph operations - # ------------------------------------------------------------------ - def add_node(self, node_id: Any, data: Dict[str, Any] | None = None) -> None: - """Add a node with optional data. - - Raises: - ValueError: If the node already exists. - """ - if node_id in self.nodes: - raise ValueError(f"Node with id {node_id} already exists") - self.nodes[node_id] = data or {} - self.edges[node_id] = set() - - def add_edge( - self, - from_id: Any, - to_id: Any, - data: Dict[str, Any] | None = None, - ) -> None: - """Add a directed edge from `from_id` to `to_id` with optional data. - - Raises: - ValueError: If either node does not exist. - """ - if from_id not in self.nodes or to_id not in self.nodes: - raise ValueError("Both nodes must exist to add an edge") - self.edges[from_id].add(to_id) - self.edge_data[(from_id, to_id)] = data or {} - - def get_neighbors(self, node_id: Any) -> List[Any]: - """Return a list of neighbor node ids for the given node.""" - if node_id not in self.nodes: - raise ValueError(f"Node with id {node_id} does not exist") - return list(self.edges[node_id]) - - def get_node(self, node_id: Any) -> Dict[str, Any] | None: - """Return the data dictionary for a node, or None if it doesn't exist.""" - return self.nodes.get(node_id) - - def get_all_nodes(self) -> List[Any]: - """Return a list of all node ids.""" - return list(self.nodes.keys()) - - def get_all_edges(self) -> List[Dict[str, Any]]: - """Return a list of all edges as dictionaries.""" - edges: List[Dict[str, Any]] = [] - for from_id, neighbors in self.edges.items(): - for to_id in neighbors: - edges.append( - { - "from": from_id, - "to": to_id, - "data": self.edge_data.get((from_id, to_id)), - } - ) - return edges - - def get_edge_data(self, from_id: Any, to_id: Any) -> Dict[str, Any] | None: - """Return the data dictionary for an edge, or None if it doesn't exist.""" - return self.edge_data.get((from_id, to_id)) - - # ------------------------------------------------------------------ - # Reflection utilities - # ------------------------------------------------------------------ - def get_properties(self) -> List[str]: - """Return the names of own instance attributes.""" - return list(self.__dict__.keys()) - - def get_methods(self) -> List[str]: - """Return the names of public methods defined on the class.""" - methods = [ - name - for name, value in vars(self.__class__).items() - if callable(value) and not name.startswith("_") - ] - return methods - - # ------------------------------------------------------------------ - # Introspection utilities - # ------------------------------------------------------------------ - def get_node_properties(self, node_id: Any) -> List[str] | None: - """Return the keys of the node's data dictionary.""" - node = self.nodes.get(node_id) - return list(node.keys()) if node is not None else None - - def get_edge_properties(self, from_id: Any, to_id: Any) -> List[str] | None: - """Return the keys of the edge's data dictionary.""" - edge = self.edge_data.get((from_id, to_id)) - return list(edge.keys()) if edge is not None else None - - -# If this module is run directly, demonstrate basic usage. -if __name__ == "__main__": - g = Graph() - g.add_node("a", {"value": 1}) - g.add_node("b", {"value": 2}) - g.add_edge("a", "b", {"weight": 5}) - print("Nodes:", g.get_all_nodes()) - print("Edges:", g.get_all_edges()) - print("Neighbors of a:", g.get_neighbors("a")) - print("Properties:", g.get_properties()) - print("Methods:", g.get_methods()) - print("Node 'a' properties:", g.get_node_properties("a")) - print("Edge a->b properties:", g.get_edge_properties("a", "b")) \ No newline at end of file diff --git a/src/index.test.js b/src/index.test.js deleted file mode 100644 index 540f704..0000000 --- a/src/index.test.js +++ /dev/null @@ -1,94 +0,0 @@ -const Graph = require('./index'); - -describe('Graph', () => { - let graph; - - beforeEach(() => { - graph = new Graph(); - }); - - test('should add nodes and retrieve them', () => { - graph.addNode('a', { value: 1 }); - graph.addNode('b', { value: 2 }); - expect(graph.getNode('a')).toEqual({ value: 1 }); - expect(graph.getNode('b')).toEqual({ value: 2 }); - expect(graph.getAllNodes()).toEqual(expect.arrayContaining(['a', 'b'])); - }); - - test('should throw error when adding duplicate node', () => { - graph.addNode('a'); - expect(() => graph.addNode('a')).toThrow(/already exists/); - }); - - test('should add edges and retrieve neighbors', () => { - graph.addNode('a'); - graph.addNode('b'); - graph.addNode('c'); - graph.addEdge('a', 'b', { weight: 5 }); - graph.addEdge('a', 'c', { weight: 3 }); - expect(graph.getNeighbors('a')).toEqual(expect.arrayContaining(['b', 'c'])); - expect(graph.getNeighbors('b')).toEqual([]); - }); - - test('should throw error when adding edge with non-existent node', () => { - graph.addNode('a'); - expect(() => graph.addEdge('a', 'x')).toThrow(/Both nodes must exist/); - }); - - test('should retrieve edge data', () => { - graph.addNode('a'); - graph.addNode('b'); - graph.addEdge('a', 'b', { weight: 10 }); - expect(graph.getEdgeData('a', 'b')).toEqual({ weight: 10 }); - }); - - test('should retrieve all edges', () => { - graph.addNode('a'); - graph.addNode('b'); - graph.addNode('c'); - graph.addEdge('a', 'b', { weight: 1 }); - graph.addEdge('b', 'c', { weight: 2 }); - const edges = graph.getAllEdges(); - expect(edges).toEqual( - expect.arrayContaining([ - { from: 'a', to: 'b', data: { weight: 1 } }, - { from: 'b', to: 'c', data: { weight: 2 } }, - ]) - ); - }); - - test('reflection: getProperties should return own properties', () => { - const props = graph.getProperties(); - expect(props).toEqual(expect.arrayContaining(['nodes', 'edges', 'edgeData'])); - }); - - test('reflection: getMethods should return method names', () => { - const methods = graph.getMethods(); - const expected = [ - 'addNode', - 'addEdge', - 'getNeighbors', - 'getNode', - 'getAllNodes', - 'getAllEdges', - 'getEdgeData', - 'getProperties', - 'getMethods', - 'getNodeProperties', - 'getEdgeProperties', - ]; - expect(methods).toEqual(expect.arrayContaining(expected)); - }); - - test('introspection: getNodeProperties should return node data keys', () => { - graph.addNode('a', { x: 1, y: 2 }); - expect(graph.getNodeProperties('a')).toEqual(expect.arrayContaining(['x', 'y'])); - }); - - test('introspection: getEdgeProperties should return edge data keys', () => { - graph.addNode('a'); - graph.addNode('b'); - graph.addEdge('a', 'b', { weight: 5, label: 'ab' }); - expect(graph.getEdgeProperties('a', 'b')).toEqual(expect.arrayContaining(['weight', 'label'])); - }); -}); \ No newline at end of file diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index f33763d..0000000 --- a/src/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { Graph } from './graph'; -export { BaseNode } from './nodes/baseNode'; -export { ReflectionNode } from './nodes/reflectionNode'; -export { RewriteNode, RewriteFunction } from './nodes/rewriteNode'; \ No newline at end of file diff --git a/src/langgraph.ts b/src/langgraph.ts deleted file mode 100644 index 627cb22..0000000 --- a/src/langgraph.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { StateGraph } from 'langgraph'; - -export type State = { - input: string; - output?: string; -}; - -const startFn = (state: State) => { - // The start node simply passes the initial state through. - return state; -}; - -const reflection = (state: State) => { - console.log('Reflection node:', state); - return state; -}; - -const rewriting = (state: State) => { - const newState = { ...state, output: state.input.toUpperCase() }; - console.log('Rewriting node:', newState); - return newState; -}; - -const end = (state: State) => { - console.log('End node:', state); - return state; -}; - -export const graph = new StateGraph(); - -graph.addNode('start', startFn); -graph.addNode('reflection', reflection); -graph.addNode('rewriting', rewriting); -graph.addNode('end', end); - -graph.setEntryPoint('start'); -graph.addEdge('start', 'reflection'); -graph.addEdge('reflection', 'rewriting'); -graph.addEdge('rewriting', 'end'); - -export const app = graph.compile(); \ No newline at end of file diff --git a/src/llm_integration.py b/src/llm_integration.py deleted file mode 100644 index a4e6e2e..0000000 --- a/src/llm_integration.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -LLM integration module for LangChain with support for OpenAI and Ollama. -Provides a reusable LLM client based on environment configuration. -""" - -import os -from typing import Union - -from langchain.llms import OpenAI, Ollama -from langchain.chat_models import ChatOpenAI, ChatOllama - -# Environment variable to select provider: "openai" or "ollama" -LLM_PROVIDER = os.getenv("LLM_PROVIDER", "openai").lower() - - -def get_llm() -> Union[OpenAI, Ollama, ChatOpenAI, ChatOllama]: - """ - Returns an LLM instance based on the configured provider. - - For OpenAI, uses the default OpenAI LLM (text-davinci-003 or gpt-3.5-turbo). - For Ollama, uses the default Ollama LLM (e.g., llama2). - - Raises: - ValueError: If an unsupported provider is specified. - """ - if LLM_PROVIDER == "openai": - # Use ChatOpenAI for GPT-3.5-turbo by default - return ChatOpenAI(temperature=0.7) - elif LLM_PROVIDER == "ollama": - # Use ChatOllama for local models - return ChatOllama(model="llama2", temperature=0.7) - else: - raise ValueError(f"Unsupported LLM provider: {LLM_PROVIDER}") \ No newline at end of file diff --git a/src/main.py b/src/main.py deleted file mode 100644 index dfc53bb..0000000 --- a/src/main.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -Entry point for running the graph with user-provided text. -""" - -import argparse -import sys - -from .graph import build_example_graph - - -def main(): - parser = argparse.ArgumentParser(description="Run the reflection and rewriting graph.") - parser.add_argument( - "text", - nargs="?", - help="Input text to process. If omitted, reads from stdin.", - ) - args = parser.parse_args() - - if args.text: - input_text = args.text - else: - input_text = sys.stdin.read() - - graph = build_example_graph() - result = graph.run(input_text) - - # The final node returns a dict with 'rewritten' key - if isinstance(result, dict) and "rewritten" in result: - print("Rewritten Text:\n") - print(result["rewritten"]) - else: - print("Result:") - print(result) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/src/nodes.js b/src/nodes.js deleted file mode 100644 index 3a42089..0000000 --- a/src/nodes.js +++ /dev/null @@ -1,42 +0,0 @@ -export class Node { - /** - * @param {string} id - Unique identifier for the node - * @param {object} [data={}] - Optional payload - */ - constructor(id, data = {}) { - if (!id) { - throw new Error('Node must have an id'); - } - this.id = id; - this.type = 'generic'; - this.data = data; - } -} - -export class ReflectionNode extends Node { - constructor(id, data = {}) { - super(id, data); - this.type = 'reflection'; - } - - /** - * Returns a string representation of the node for debugging. - */ - toString() { - return `ReflectionNode(${this.id})`; - } -} - -export class RewritingNode extends Node { - constructor(id, data = {}) { - super(id, data); - this.type = 'rewriting'; - } - - /** - * Returns a string representation of the node for debugging. - */ - toString() { - return `RewritingNode(${this.id})`; - } -} \ No newline at end of file diff --git a/src/nodes.py b/src/nodes.py deleted file mode 100644 index 4c3e009..0000000 --- a/src/nodes.py +++ /dev/null @@ -1,83 +0,0 @@ -""" -Node definitions for the graph. -Includes base Node, ReflectionNode, RewritingNode, InputNode, and OutputNode. -""" - -from abc import ABC, abstractmethod -from typing import Any, Dict - -from .llm_integration import get_llm - - -class BaseNode(ABC): - """ - Abstract base class for all nodes in the graph. - Each node must implement the `process` method. - """ - - def __init__(self, node_id: str): - self.node_id = node_id - - @abstractmethod - def process(self, input_data: Any) -> Any: - """ - Process the input data and return the output. - """ - pass - - -class InputNode(BaseNode): - """ - Node that simply passes through the input data. - """ - - def process(self, input_data: Any) -> Any: - return input_data - - -class OutputNode(BaseNode): - """ - Node that collects the final output. - """ - - def process(self, input_data: Any) -> Any: - return input_data - - -class ReflectionNode(BaseNode): - """ - Node that generates reflective insights from the input text using an LLM. - """ - - def __init__(self, node_id: str, prompt_template: str = None): - super().__init__(node_id) - self.prompt_template = ( - prompt_template - or "Please reflect on the following text:\n\n{input_text}\n\nReflection:" - ) - self.llm = get_llm() - - def process(self, input_data: str) -> Dict[str, str]: - prompt = self.prompt_template.format(input_text=input_data) - reflection = self.llm(prompt) - return {"reflection": reflection.strip()} - - -class RewritingNode(BaseNode): - """ - Node that rewrites the input text according to a specified style or instruction. - """ - - def __init__(self, node_id: str, style: str = "formal"): - super().__init__(node_id) - self.style = style - self.llm = get_llm() - - def process(self, input_data: Dict[str, str]) -> Dict[str, str]: - # Expecting input_data to contain 'reflection' key - reflection = input_data.get("reflection", "") - prompt = ( - f"Rewrite the following reflection in a {self.style} style:\n\n{reflection}\n\nRewritten:" - ) - rewritten = self.llm(prompt) - return {"rewritten": rewritten.strip()} \ No newline at end of file diff --git a/src/nodes/baseNode.js b/src/nodes/baseNode.js deleted file mode 100644 index 5c1eaf5..0000000 --- a/src/nodes/baseNode.js +++ /dev/null @@ -1,12 +0,0 @@ -class BaseNode { - constructor(name, graph) { - this.name = name; - this.graph = graph; - } - - evaluate(input) { - throw new Error('evaluate() must be implemented by subclass'); - } -} - -module.exports = BaseNode; \ No newline at end of file diff --git a/src/nodes/baseNode.ts b/src/nodes/baseNode.ts deleted file mode 100644 index 0b75320..0000000 --- a/src/nodes/baseNode.ts +++ /dev/null @@ -1,15 +0,0 @@ -export abstract class BaseNode { - id: string; - type: string; - inputs: Map; - outputs: Map; - - constructor(id: string, type: string) { - this.id = id; - this.type = type; - this.inputs = new Map(); - this.outputs = new Map(); - } - - abstract process(): void; -} \ No newline at end of file diff --git a/src/nodes/reflect.js b/src/nodes/reflect.js deleted file mode 100644 index 9fdd44d..0000000 --- a/src/nodes/reflect.js +++ /dev/null @@ -1,35 +0,0 @@ -const { OpenAI } = require('langchain-openai'); -const { ChatPromptTemplate, HumanMessagePromptTemplate } = require('langchain-core/prompts'); -const { LLMChain } = require('langchain-core/chains'); - -// Initialize the LLM (OpenAI) with a moderate temperature for reflective responses -const llm = new OpenAI({ temperature: 0.7 }); - -// Prompt template for reflection -const prompt = ChatPromptTemplate.fromPromptMessages([ - HumanMessagePromptTemplate.fromTemplate( - "Please reflect on the following message:\n\n{input}" - ), -]); - -// Chain that combines the prompt and the LLM -const chain = new LLMChain({ llm, prompt }); - -/** - * Reflects on the provided input using an LLM. - * @param {string} input - The message to reflect upon. - * @returns {Promise} - The reflective output from the LLM. - */ -async function reflect(input) { - if (typeof input !== 'string') { - throw new Error('Reflect node expects a string input.'); - } - try { - const result = await chain.invoke({ input }); - return result.output; - } catch (err) { - throw new Error(`Reflect node error: ${err.message}`); - } -} - -module.exports = { reflect }; \ No newline at end of file diff --git a/src/nodes/reflect.py b/src/nodes/reflect.py deleted file mode 100644 index 48add9f..0000000 --- a/src/nodes/reflect.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Reflect node for LangGraph. - -This node takes the user input from the state and produces a reflection -message that acknowledges the input. The output is a dictionary containing -the key 'reflection'. -""" - -from langgraph.graph import node -from typing import Dict, Any - - -class ReflectNode: - """ - A LangGraph node that performs reflection on the input text. - """ - - @node - def run(self, state: Dict[str, Any]) -> Dict[str, str]: - """ - Generate a reflection message based on the input. - - Parameters - ---------- - state : dict - The current state of the graph. Expected to contain an 'input' - key with the user-provided text. - - Returns - ------- - dict - A dictionary with a single key 'reflection' containing the - reflection message. - """ - input_text = state.get("input", "") - reflection = ( - f"I see that you said: '{input_text}'. " - "Let's reflect on that." - ) - return {"reflection": reflection} \ No newline at end of file diff --git a/src/nodes/reflectionNode.js b/src/nodes/reflectionNode.js deleted file mode 100644 index c1162e8..0000000 --- a/src/nodes/reflectionNode.js +++ /dev/null @@ -1,19 +0,0 @@ -export default class ReflectionNode { - /** - * Creates a new ReflectionNode. - * @param {string} id - Unique identifier for the node. - */ - constructor(id) { - this.id = id; - this.type = 'reflection'; - } - - /** - * Processes the input and returns it unchanged. - * @param {*} input - The input value from the preceding node(s). - * @returns {*} The same input value. - */ - process(input) { - return input; - } -} \ No newline at end of file diff --git a/src/nodes/reflectionNode.ts b/src/nodes/reflectionNode.ts deleted file mode 100644 index 0d40510..0000000 --- a/src/nodes/reflectionNode.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BaseNode } from './baseNode'; - -export class ReflectionNode extends BaseNode { - constructor(id: string) { - super(id, 'reflection'); - } - - process(): void { - // Copy all inputs to outputs with the same keys - this.inputs.forEach((value, key) => { - this.outputs.set(key, value); - }); - } -} \ No newline at end of file diff --git a/src/nodes/rewrite.js b/src/nodes/rewrite.js deleted file mode 100644 index bba83c0..0000000 --- a/src/nodes/rewrite.js +++ /dev/null @@ -1,35 +0,0 @@ -const { OpenAI } = require('langchain-openai'); -const { ChatPromptTemplate, HumanMessagePromptTemplate } = require('langchain-core/prompts'); -const { LLMChain } = require('langchain-core/chains'); - -// Initialize the LLM (OpenAI) with a moderate temperature for rewriting -const llm = new OpenAI({ temperature: 0.7 }); - -// Prompt template for rewriting -const prompt = ChatPromptTemplate.fromPromptMessages([ - HumanMessagePromptTemplate.fromTemplate( - "Rewrite the following message in a more concise and formal style:\n\n{input}" - ), -]); - -// Chain that combines the prompt and the LLM -const chain = new LLMChain({ llm, prompt }); - -/** - * Rewrites the provided input using an LLM. - * @param {string} input - The message to rewrite. - * @returns {Promise} - The rewritten output from the LLM. - */ -async function rewrite(input) { - if (typeof input !== 'string') { - throw new Error('Rewrite node expects a string input.'); - } - try { - const result = await chain.invoke({ input }); - return result.output; - } catch (err) { - throw new Error(`Rewrite node error: ${err.message}`); - } -} - -module.exports = { rewrite }; \ No newline at end of file diff --git a/src/nodes/rewrite.py b/src/nodes/rewrite.py deleted file mode 100644 index ce2f121..0000000 --- a/src/nodes/rewrite.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -Rewrite node for LangGraph. - -This node takes the reflection produced by the ReflectNode and rewrites -it to a more formal style. The output is a dictionary containing -the key 'rewritten'. -""" - -from langgraph.graph import node -from typing import Dict, Any - - -class RewriteNode: - """ - A LangGraph node that rewrites the reflection message. - """ - - @node - def run(self, state: Dict[str, Any]) -> Dict[str, str]: - """ - Rewrite the reflection message. - - Parameters - ---------- - state : dict - The current state of the graph. Expected to contain a 'reflection' - key with the message produced by the ReflectNode. - - Returns - ------- - dict - A dictionary with a single key 'rewritten' containing the - rewritten message. - """ - reflection = state.get("reflection", "") - # Simple rewrite: replace "I see" with "I notice" - rewritten = reflection.replace("I see", "I notice") - return {"rewritten": rewritten} \ No newline at end of file diff --git a/src/nodes/rewriteNode.js b/src/nodes/rewriteNode.js deleted file mode 100644 index f9ff378..0000000 --- a/src/nodes/rewriteNode.js +++ /dev/null @@ -1,21 +0,0 @@ -export default class RewriteNode { - /** - * Creates a new RewriteNode. - * @param {string} id - Unique identifier for the node. - * @param {function} transform - Function that transforms the input. - */ - constructor(id, transform) { - this.id = id; - this.type = 'rewrite'; - this.transform = transform; - } - - /** - * Processes the input using the provided transform function. - * @param {*} input - The input value from the preceding node(s). - * @returns {*} The transformed output. - */ - process(input) { - return this.transform(input); - } -} \ No newline at end of file diff --git a/src/nodes/rewriteNode.ts b/src/nodes/rewriteNode.ts deleted file mode 100644 index c4265e7..0000000 --- a/src/nodes/rewriteNode.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { BaseNode } from './baseNode'; - -export type RewriteFunction = (value: any) => any; - -export class RewriteNode extends BaseNode { - private func: RewriteFunction; - - constructor(id: string, func: RewriteFunction) { - super(id, 'rewrite'); - this.func = func; - } - - process(): void { - this.inputs.forEach((value, key) => { - const newValue = this.func(value); - this.outputs.set(key, newValue); - }); - } -} \ No newline at end of file diff --git a/src/unrelated.js b/src/unrelated.js deleted file mode 100644 index 2a78c21..0000000 --- a/src/unrelated.js +++ /dev/null @@ -1,3 +0,0 @@ -// This file has been removed from the project as it contained unrelated JavaScript code. -// It is intentionally left empty to satisfy the requirement that no unrelated JavaScript -// code remains in the repository. \ No newline at end of file diff --git a/src/utils.js b/src/utils.js deleted file mode 100644 index 8114706..0000000 --- a/src/utils.js +++ /dev/null @@ -1,3 +0,0 @@ -// Utility functions can be added here if needed in the future. -// Currently, no utilities are required for the core graph functionality. -module.exports = {}; \ No newline at end of file diff --git a/src/utils.py b/src/utils.py deleted file mode 100644 index b13a792..0000000 --- a/src/utils.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -Utility functions for the LangGraph project. -""" - -from langchain_openai import ChatOpenAI -from typing import Dict, Any - -def get_llm() -> ChatOpenAI: - """ - Returns a configured OpenAI LLM instance. - """ - # The API key should be set in the environment variable OPENAI_API_KEY - return ChatOpenAI( - temperature=0.7, - model_name="gpt-3.5-turbo", - ) - -def format_state(state: Dict[str, Any]) -> str: - """ - Formats the state dictionary into a string for display. - """ - return "\n".join(f"{k}: {v}" for k, v in state.items()) \ No newline at end of file