feat: solution for 'Повторный экзамен #2: Граф с рефлексией на код'
This commit is contained in:
@@ -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
|
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
|
||||||
|
|
||||||
|
К списку заданий
|
||||||
@@ -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
@@ -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;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const Graph = require('./graph');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Graph
|
||||||
|
};
|
||||||
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user