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
|
||||
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.*
|
||||
ВЕРСИЯ
|
||||
|
||||
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