From 224adca90a94badfccbddaabcd92c69d4aa73771 Mon Sep 17 00:00:00 2001 From: kuzakhmetovartur Date: Tue, 30 Jun 2026 11:39:42 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20solution=20for=20'=D0=9F=D0=BE=D0=B2?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=BD=D1=8B=D0=B9=20=D1=8D=D0=BA=D0=B7=D0=B0?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=20#2:=20=D0=93=D1=80=D0=B0=D1=84=20=D1=81=20?= =?UTF-8?q?=D1=80=D0=B5=D1=84=D0=BB=D0=B5=D0=BA=D1=81=D0=B8=D0=B5=D0=B9=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=B4'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 127 ++++++++++++++++++++----------------- package.json | 19 ++++++ src/graph.js | 153 +++++++++++++++++++++++++++++++++++++++++++++ src/index.js | 5 ++ test/graph.test.js | 79 +++++++++++++++++++++++ 5 files changed, 327 insertions(+), 56 deletions(-) create mode 100644 package.json create mode 100644 src/graph.js create mode 100644 src/index.js create mode 100644 test/graph.test.js diff --git a/README.md b/README.md index 1fe806d..92faac7 100644 --- a/README.md +++ b/README.md @@ -1,81 +1,96 @@ -# Graph with Reflection on Code +# Повторный экзамен #2: Граф с рефлексией на код -This repository demonstrates how to build a conversational agent that can analyze and reflect on Python code using **LangGraph** and **LangChain OpenAI**. The agent can parse code, generate explanations, and answer questions about the code structure. +## Original assignment -## Features +Главная +Мои задания +Повторный экзамен #2: Граф с рефлексией на код +5Д +EN +Повторный экзамен #2: Граф с рефлексией на код +Зачёт +Версия 5 +Дедлайн сдачи: 31.08.2026 -- **LangGraph**: Orchestrates the conversation flow and manages state across multiple turns. -- **LangChain OpenAI**: Provides language model capabilities via OpenAI’s GPT-4 (or any compatible model). -- Code parsing and analysis using the `ast` module. -- Interactive CLI for asking questions about a Python file. +В работе -## Getting Started +Требуется доработка -### Prerequisites +В решении одновременно используются оба подхода. Приведите реализацию к одному варианту в соответствии с условием задания. -- Python 3.10+ -- An OpenAI API key. Set it in your environment: +Редактирование ответа -```bash -export OPENAI_API_KEY="your_api_key_here" -``` +Заполните ответ и отправьте работу на проверку преподавателю. -### Installation +Тип ответа +Текст +Ссылка +Файлы +Ссылка (URL) +Прикреплённые файлы +Загрузить файл +Отправить на проверку +Отменить -```bash -# Clone the repository -git clone https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-graf-s-refleksiey-na.git -cd povtornyy-ekzamen-2-graf-s-refleksiey-na +ПОДРОБНЕЕ -# Create a virtual environment (optional but recommended) -python -m venv .venv -source .venv/bin/activate # On Windows use `.venv\Scripts\activate` +Задание +Предыдущие версии +ВЕРСИЯ 4 -# Install dependencies -pip install -r requirements.txt -``` +30.06.2026, 11:31 -`requirements.txt` contains: +https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-graf-s-refleksiey-na +ВЕРСИЯ 3 -``` -langchain==0.2.0 -langgraph==0.1.0 -openai==1.0.0 -``` +30.06.2026, 00:23 -### Usage +https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-graf-s-refleksiey-na +ВЕРСИЯ 2 -Run the main script and provide the path to a Python file you want to analyze: +29.06.2026, 17:40 -```bash -python main.py path/to/your_script.py -``` +https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-graf-s-refleksiey-na +1 -You will be prompted to ask questions about the code. The agent will respond using the OpenAI model and the conversation graph. +В работе -### Example +2 -```bash -$ python main.py example.py -Enter your question (or type 'exit' to quit): What does the `add` function do? -The `add` function takes two numbers, `a` and `b`, and returns their sum. -``` +На проверке -## Project Structure +3 -``` -povtornyy-ekzamen-2-graf-s-refleksiey-na/ -├── main.py # Entry point -├── code_analyzer.py # Code parsing utilities -├── graph.py # LangGraph definition -├── requirements.txt -└── README.md -``` +Завершено -## License +Сводка -This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. +СТАТУС ---- +В работе -*This project was developed as part of a coursework assignment. It showcases the integration of LangGraph and LangChain OpenAI for code analysis and reflection.* \ No newline at end of file +ВЕРСИЯ + +5 + +СОЗДАНО + +23.06.2026, 14:49 + +ПОСЛЕДНЯЯ СДАЧА + +— + +ИЗМЕНЕНО + +30.06.2026, 11:32 + +ТИП ЗАДАНИЯ + +Индивидуальное + +ЛЕКЦИЙ + +Повторный экзамен #2 · 11.06.2026, 18:30 + +К списку заданий \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..4c4991c --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "graph-reflexive", + "version": "1.0.0", + "description": "Graph implementation with reflexive property using adjacency list", + "main": "src/index.js", + "scripts": { + "test": "jest" + }, + "keywords": [ + "graph", + "reflexive", + "adjacency-list" + ], + "author": "Auto-generated", + "license": "MIT", + "devDependencies": { + "jest": "^29.7.0" + } +} \ No newline at end of file diff --git a/src/graph.js b/src/graph.js new file mode 100644 index 0000000..aead4fb --- /dev/null +++ b/src/graph.js @@ -0,0 +1,153 @@ +/** + * Graph implementation using adjacency list. + * Each vertex automatically has a self-loop (reflexive edge). + * The graph is undirected. + */ +class Graph { + constructor() { + /** @type {Map<*, Set<*>>} */ + this.adj = new Map(); + } + + /** + * Adds a vertex to the graph. + * If the vertex already exists, nothing changes. + * A self-loop is automatically added to make the graph reflexive. + * @param {*} v + */ + addVertex(v) { + if (!this.adj.has(v)) { + this.adj.set(v, new Set([v])); + } + } + + /** + * Adds an undirected edge between u and v. + * Vertices are added automatically if they do not exist. + * @param {*} u + * @param {*} v + */ + addEdge(u, v) { + this.addVertex(u); + this.addVertex(v); + this.adj.get(u).add(v); + this.adj.get(v).add(u); + } + + /** + * Removes the undirected edge between u and v. + * If the edge does not exist, nothing happens. + * @param {*} u + * @param {*} v + */ + removeEdge(u, v) { + if (this.adj.has(u)) this.adj.get(u).delete(v); + if (this.adj.has(v)) this.adj.get(v).delete(u); + } + + /** + * Removes a vertex and all incident edges. + * @param {*} v + */ + removeVertex(v) { + if (!this.adj.has(v)) return; + for (const neighbor of this.adj.get(v)) { + if (neighbor !== v) this.adj.get(neighbor).delete(v); + } + this.adj.delete(v); + } + + /** + * Checks whether an edge exists between u and v. + * @param {*} u + * @param {*} v + * @returns {boolean} + */ + hasEdge(u, v) { + return this.adj.has(u) && this.adj.get(u).has(v); + } + + /** + * Returns an array of neighbors of vertex v. + * @param {*} v + * @returns {Array<*>} + */ + getNeighbors(v) { + return this.adj.has(v) ? Array.from(this.adj.get(v)) : []; + } + + /** + * Returns an array of all vertices in the graph. + * @returns {Array<*>} + */ + vertices() { + return Array.from(this.adj.keys()); + } + + /** + * Returns an array of all edges as [u, v] pairs. + * Each undirected edge appears only once. + * @returns {Array<[*, *]>} + */ + edges() { + const edges = []; + const seen = new Set(); + for (const [u, neighbors] of this.adj.entries()) { + for (const v of neighbors) { + const key = u < v ? `${u}-${v}` : `${v}-${u}`; + if (!seen.has(key)) { + edges.push([u, v]); + seen.add(key); + } + } + } + return edges; + } + + /** + * Returns the number of vertices. + * @returns {number} + */ + size() { + return this.adj.size; + } + + /** + * Returns the number of undirected edges. + * @returns {number} + */ + edgesCount() { + return this.edges().length; + } + + /** + * Checks whether the graph is reflexive (every vertex has a self-loop). + * @returns {boolean} + */ + isReflexive() { + for (const [v, neighbors] of this.adj.entries()) { + if (!neighbors.has(v)) return false; + } + return true; + } + + /** + * Adds self-loops to all vertices, making the graph reflexive. + */ + makeReflexive() { + for (const v of this.adj.keys()) { + this.adj.get(v).add(v); + } + } + + /** + * Removes self-loops from all vertices. + */ + removeReflexive() { + for (const [v, neighbors] of this.adj.entries()) { + neighbors.delete(v); + } + } +} + +module.exports = Graph; \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..2dda1cb --- /dev/null +++ b/src/index.js @@ -0,0 +1,5 @@ +const Graph = require('./graph'); + +module.exports = { + Graph +}; \ No newline at end of file diff --git a/test/graph.test.js b/test/graph.test.js new file mode 100644 index 0000000..3ad6447 --- /dev/null +++ b/test/graph.test.js @@ -0,0 +1,79 @@ +const { Graph } = require('../src'); + +describe('Graph', () => { + let g; + + beforeEach(() => { + g = new Graph(); + }); + + test('initially empty', () => { + expect(g.size()).toBe(0); + expect(g.edgesCount()).toBe(0); + }); + + test('addVertex increases size and adds reflexive edge', () => { + g.addVertex('a'); + expect(g.size()).toBe(1); + expect(g.isReflexive()).toBe(true); + expect(g.hasEdge('a', 'a')).toBe(true); + }); + + test('addEdge connects vertices and updates adjacency', () => { + g.addEdge('a', 'b'); + expect(g.size()).toBe(2); + expect(g.hasEdge('a', 'b')).toBe(true); + expect(g.hasEdge('b', 'a')).toBe(true); + expect(g.getNeighbors('a')).toEqual(expect.arrayContaining(['a', 'b'])); + expect(g.getNeighbors('b')).toEqual(expect.arrayContaining(['a', 'b'])); + }); + + test('removeEdge removes connection', () => { + g.addEdge('a', 'b'); + g.removeEdge('a', 'b'); + expect(g.hasEdge('a', 'b')).toBe(false); + expect(g.hasEdge('b', 'a')).toBe(false); + // self-loops remain + expect(g.hasEdge('a', 'a')).toBe(true); + expect(g.hasEdge('b', 'b')).toBe(true); + }); + + test('removeVertex removes vertex and incident edges', () => { + g.addEdge('a', 'b'); + g.addEdge('a', 'c'); + g.removeVertex('a'); + expect(g.size()).toBe(2); + expect(g.hasEdge('b', 'a')).toBe(false); + expect(g.hasEdge('c', 'a')).toBe(false); + expect(g.hasEdge('b', 'c')).toBe(false); + }); + + test('edges and edgesCount work correctly', () => { + g.addEdge('a', 'b'); + g.addEdge('b', 'c'); + g.addEdge('c', 'a'); + expect(g.edgesCount()).toBe(3); + const edges = g.edges(); + expect(edges).toEqual(expect.arrayContaining([ + ['a', 'b'], + ['b', 'c'], + ['c', 'a'] + ])); + }); + + test('self-loop handling', () => { + g.addVertex('x'); + expect(g.hasEdge('x', 'x')).toBe(true); + g.removeEdge('x', 'x'); + expect(g.hasEdge('x', 'x')).toBe(false); + }); + + test('reflexivity can be toggled', () => { + g.addVertex('p'); + g.addVertex('q'); + g.removeReflexive(); + expect(g.isReflexive()).toBe(false); + g.makeReflexive(); + expect(g.isReflexive()).toBe(true); + }); +}); \ No newline at end of file