feat: solution for 'Повторный экзамен #2: Граф с рефлексией на код'

This commit is contained in:
2026-06-30 11:39:42 +03:00
parent b09469b196
commit 224adca90a
5 changed files with 327 additions and 56 deletions
+71 -56
View File
@@ -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: Граф с рефлексией на код
EN
Повторный экзамен #2: Граф с рефлексией на код
Зачёт
Версия 5
Дедлайн сдачи: 31.08.2026
- **LangGraph**: Orchestrates the conversation flow and manages state across multiple turns. В работе
- **LangChain OpenAI**: Provides language model capabilities via OpenAIs 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 30.06.2026, 11:31
pip install -r requirements.txt
```
`requirements.txt` contains: https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-graf-s-refleksiey-na
ВЕРСИЯ 3
``` 30.06.2026, 00:23
langchain==0.2.0
langgraph==0.1.0
openai==1.0.0
```
### 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 https://git.brojs.ru/kuzakhmetovartur/povtornyy-ekzamen-2-graf-s-refleksiey-na
python main.py path/to/your_script.py 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.* ВЕРСИЯ
5
СОЗДАНО
23.06.2026, 14:49
ПОСЛЕДНЯЯ СДАЧА
ИЗМЕНЕНО
30.06.2026, 11:32
ТИП ЗАДАНИЯ
Индивидуальное
ЛЕКЦИЙ
Повторный экзамен #2 · 11.06.2026, 18:30
К списку заданий
+19
View File
@@ -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"
}
}
+153
View File
@@ -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;
+5
View File
@@ -0,0 +1,5 @@
const Graph = require('./graph');
module.exports = {
Graph
};
+79
View File
@@ -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);
});
});