dialograph 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dialograph-0.3.0/PKG-INFO +173 -0
- dialograph-0.3.0/README.md +162 -0
- dialograph-0.3.0/pyproject.toml +32 -0
- dialograph-0.3.0/setup.cfg +4 -0
- dialograph-0.3.0/src/dialograph/__init__.py +4 -0
- dialograph-0.3.0/src/dialograph/agent/__init__.py +0 -0
- dialograph-0.3.0/src/dialograph/agent/agent.py +58 -0
- dialograph-0.3.0/src/dialograph/agent/policy.py +76 -0
- dialograph-0.3.0/src/dialograph/agent/prompts.py +0 -0
- dialograph-0.3.0/src/dialograph/core/__init__.py +7 -0
- dialograph-0.3.0/src/dialograph/core/draw.py +7 -0
- dialograph-0.3.0/src/dialograph/core/edge.py +232 -0
- dialograph-0.3.0/src/dialograph/core/graph.py +93 -0
- dialograph-0.3.0/src/dialograph/core/node.py +77 -0
- dialograph-0.3.0/src/dialograph/memory/__init__.py +0 -0
- dialograph-0.3.0/src/dialograph/memory/belief.py +13 -0
- dialograph-0.3.0/src/dialograph/memory/preference.py +20 -0
- dialograph-0.3.0/src/dialograph/memory/strategy.py +11 -0
- dialograph-0.3.0/src/dialograph/traversal/__init__.py +0 -0
- dialograph-0.3.0/src/dialograph/traversal/retrieve.py +24 -0
- dialograph-0.3.0/src/dialograph/traversal/score.py +21 -0
- dialograph-0.3.0/src/dialograph/utils/__init__.py +0 -0
- dialograph-0.3.0/src/dialograph/utils/export.py +4 -0
- dialograph-0.3.0/src/dialograph/utils/io.py +10 -0
- dialograph-0.3.0/src/dialograph.egg-info/PKG-INFO +173 -0
- dialograph-0.3.0/src/dialograph.egg-info/SOURCES.txt +29 -0
- dialograph-0.3.0/src/dialograph.egg-info/dependency_links.txt +1 -0
- dialograph-0.3.0/src/dialograph.egg-info/requires.txt +3 -0
- dialograph-0.3.0/src/dialograph.egg-info/top_level.txt +1 -0
- dialograph-0.3.0/tests/test_graph.py +334 -0
- dialograph-0.3.0/tests/test_node_state.py +69 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dialograph
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Dialograph is a Python toolkit for building, evolving, and reasoning over temporal dialogue graphs for proactive conversational agents.
|
|
5
|
+
Author: author
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: networkx>=3.6.1
|
|
9
|
+
Requires-Dist: numpy>=2.4.0
|
|
10
|
+
Requires-Dist: pydantic>=2.12.5
|
|
11
|
+
|
|
12
|
+
# dialograph
|
|
13
|
+
|
|
14
|
+
**Dialograph** is a lightweight Python library for representing, evolving, and traversing dialogue memory as a temporal graph.
|
|
15
|
+
It is designed for **proactive dialogue agents**, where reasoning over user preferences, beliefs, and strategies matters more than raw generation.
|
|
16
|
+
|
|
17
|
+
At its core, Dialograph wraps a dynamic graph structure around typed dialogue memories and provides clean hooks for retrieval, scoring, and long-term evolution.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Core Concepts
|
|
22
|
+
|
|
23
|
+
### Nodes
|
|
24
|
+
|
|
25
|
+
Nodes represent dialogue memory units such as:
|
|
26
|
+
|
|
27
|
+
* user preferences
|
|
28
|
+
* beliefs / facts
|
|
29
|
+
* dialogue strategies
|
|
30
|
+
|
|
31
|
+
Each node carries a **state object** (e.g. `PreferenceState`, `BeliefState`) with confidence and temporal metadata.
|
|
32
|
+
|
|
33
|
+
### Edges
|
|
34
|
+
|
|
35
|
+
Edges represent relations between memory units:
|
|
36
|
+
|
|
37
|
+
* semantic relations (supports, contradicts, elicits)
|
|
38
|
+
* dialogue flow dependencies
|
|
39
|
+
* strategy activation paths
|
|
40
|
+
|
|
41
|
+
Edges are directional, weighted, and time-aware.
|
|
42
|
+
|
|
43
|
+
### Time
|
|
44
|
+
|
|
45
|
+
Dialograph maintains an internal time counter that allows:
|
|
46
|
+
|
|
47
|
+
* decay of confidence
|
|
48
|
+
* forgetting
|
|
49
|
+
* recency-based retrieval
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Project Structure
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
dialograph/
|
|
57
|
+
├── core/ # graph primitives
|
|
58
|
+
│ ├── graph.py # Dialograph wrapper
|
|
59
|
+
│ ├── node.py # node state definitions
|
|
60
|
+
│ └── edge.py # edge state definitions
|
|
61
|
+
│
|
|
62
|
+
├── memory/ # typed dialogue memory
|
|
63
|
+
│ ├── preference.py
|
|
64
|
+
│ ├── belief.py
|
|
65
|
+
│ └── strategy.py
|
|
66
|
+
│
|
|
67
|
+
├── traversal/ # retrieval and scoring
|
|
68
|
+
│ ├── retrieve.py
|
|
69
|
+
│ └── score.py
|
|
70
|
+
│
|
|
71
|
+
├── utils/ # persistence & visualization
|
|
72
|
+
│ ├── io.py
|
|
73
|
+
│ └── visualize.py
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
For development:
|
|
81
|
+
```bash
|
|
82
|
+
git clone https://github.com/nabin2004/dialograph.git
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
cd dialograph
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
uv venv
|
|
91
|
+
```
|
|
92
|
+
```bash
|
|
93
|
+
source .venv/bin/activate
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
uv pip install -e .
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Quick Example
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from dialograph.core.graph import Dialograph
|
|
106
|
+
from dialograph.memory.preference import PreferenceState
|
|
107
|
+
from dialograph.core.edge import EdgeState
|
|
108
|
+
|
|
109
|
+
g = Dialograph()
|
|
110
|
+
|
|
111
|
+
g.add_node(
|
|
112
|
+
"pref_movie",
|
|
113
|
+
state=PreferenceState(
|
|
114
|
+
key="movie_genre",
|
|
115
|
+
value="sci-fi",
|
|
116
|
+
confidence=0.9,
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
g.add_node(
|
|
121
|
+
"belief_stress",
|
|
122
|
+
state=BeliefState(
|
|
123
|
+
proposition="user is stressed about exams",
|
|
124
|
+
confidence=0.7,
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
g.add_edge(
|
|
129
|
+
"belief_stress",
|
|
130
|
+
"pref_movie",
|
|
131
|
+
state=EdgeState(relation="influences")
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Intended Use Cases
|
|
138
|
+
|
|
139
|
+
* Proactive dialogue systems
|
|
140
|
+
* Emotional support agents
|
|
141
|
+
* Preference elicitation
|
|
142
|
+
* Strategy planning and reuse
|
|
143
|
+
* Dialogue memory research
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Status
|
|
148
|
+
|
|
149
|
+
This project is **under active development**.
|
|
150
|
+
APIs may evolve, but core abstractions are expected to remain stable.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Roadmap
|
|
155
|
+
|
|
156
|
+
* [ ] Path-based retrieval
|
|
157
|
+
* [ ] Forgetting thresholds
|
|
158
|
+
* [ ] Graph serialization
|
|
159
|
+
* [ ] 3D / interactive visualization
|
|
160
|
+
* [ ] LLM-facing retrieval API
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT License.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Citation
|
|
171
|
+
|
|
172
|
+
If you use Dialograph in academic work, please cite the corresponding paper (coming soon).
|
|
173
|
+
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# dialograph
|
|
2
|
+
|
|
3
|
+
**Dialograph** is a lightweight Python library for representing, evolving, and traversing dialogue memory as a temporal graph.
|
|
4
|
+
It is designed for **proactive dialogue agents**, where reasoning over user preferences, beliefs, and strategies matters more than raw generation.
|
|
5
|
+
|
|
6
|
+
At its core, Dialograph wraps a dynamic graph structure around typed dialogue memories and provides clean hooks for retrieval, scoring, and long-term evolution.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Core Concepts
|
|
11
|
+
|
|
12
|
+
### Nodes
|
|
13
|
+
|
|
14
|
+
Nodes represent dialogue memory units such as:
|
|
15
|
+
|
|
16
|
+
* user preferences
|
|
17
|
+
* beliefs / facts
|
|
18
|
+
* dialogue strategies
|
|
19
|
+
|
|
20
|
+
Each node carries a **state object** (e.g. `PreferenceState`, `BeliefState`) with confidence and temporal metadata.
|
|
21
|
+
|
|
22
|
+
### Edges
|
|
23
|
+
|
|
24
|
+
Edges represent relations between memory units:
|
|
25
|
+
|
|
26
|
+
* semantic relations (supports, contradicts, elicits)
|
|
27
|
+
* dialogue flow dependencies
|
|
28
|
+
* strategy activation paths
|
|
29
|
+
|
|
30
|
+
Edges are directional, weighted, and time-aware.
|
|
31
|
+
|
|
32
|
+
### Time
|
|
33
|
+
|
|
34
|
+
Dialograph maintains an internal time counter that allows:
|
|
35
|
+
|
|
36
|
+
* decay of confidence
|
|
37
|
+
* forgetting
|
|
38
|
+
* recency-based retrieval
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Project Structure
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
dialograph/
|
|
46
|
+
├── core/ # graph primitives
|
|
47
|
+
│ ├── graph.py # Dialograph wrapper
|
|
48
|
+
│ ├── node.py # node state definitions
|
|
49
|
+
│ └── edge.py # edge state definitions
|
|
50
|
+
│
|
|
51
|
+
├── memory/ # typed dialogue memory
|
|
52
|
+
│ ├── preference.py
|
|
53
|
+
│ ├── belief.py
|
|
54
|
+
│ └── strategy.py
|
|
55
|
+
│
|
|
56
|
+
├── traversal/ # retrieval and scoring
|
|
57
|
+
│ ├── retrieve.py
|
|
58
|
+
│ └── score.py
|
|
59
|
+
│
|
|
60
|
+
├── utils/ # persistence & visualization
|
|
61
|
+
│ ├── io.py
|
|
62
|
+
│ └── visualize.py
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
For development:
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://github.com/nabin2004/dialograph.git
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
cd dialograph
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
uv venv
|
|
80
|
+
```
|
|
81
|
+
```bash
|
|
82
|
+
source .venv/bin/activate
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
uv pip install -e .
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Quick Example
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from dialograph.core.graph import Dialograph
|
|
95
|
+
from dialograph.memory.preference import PreferenceState
|
|
96
|
+
from dialograph.core.edge import EdgeState
|
|
97
|
+
|
|
98
|
+
g = Dialograph()
|
|
99
|
+
|
|
100
|
+
g.add_node(
|
|
101
|
+
"pref_movie",
|
|
102
|
+
state=PreferenceState(
|
|
103
|
+
key="movie_genre",
|
|
104
|
+
value="sci-fi",
|
|
105
|
+
confidence=0.9,
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
g.add_node(
|
|
110
|
+
"belief_stress",
|
|
111
|
+
state=BeliefState(
|
|
112
|
+
proposition="user is stressed about exams",
|
|
113
|
+
confidence=0.7,
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
g.add_edge(
|
|
118
|
+
"belief_stress",
|
|
119
|
+
"pref_movie",
|
|
120
|
+
state=EdgeState(relation="influences")
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Intended Use Cases
|
|
127
|
+
|
|
128
|
+
* Proactive dialogue systems
|
|
129
|
+
* Emotional support agents
|
|
130
|
+
* Preference elicitation
|
|
131
|
+
* Strategy planning and reuse
|
|
132
|
+
* Dialogue memory research
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Status
|
|
137
|
+
|
|
138
|
+
This project is **under active development**.
|
|
139
|
+
APIs may evolve, but core abstractions are expected to remain stable.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Roadmap
|
|
144
|
+
|
|
145
|
+
* [ ] Path-based retrieval
|
|
146
|
+
* [ ] Forgetting thresholds
|
|
147
|
+
* [ ] Graph serialization
|
|
148
|
+
* [ ] 3D / interactive visualization
|
|
149
|
+
* [ ] LLM-facing retrieval API
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## License
|
|
154
|
+
|
|
155
|
+
MIT License.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Citation
|
|
160
|
+
|
|
161
|
+
If you use Dialograph in academic work, please cite the corresponding paper (coming soon).
|
|
162
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "dialograph"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
description = "Dialograph is a Python toolkit for building, evolving, and reasoning over temporal dialogue graphs for proactive conversational agents."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "author" }
|
|
9
|
+
]
|
|
10
|
+
dependencies = [
|
|
11
|
+
"networkx>=3.6.1",
|
|
12
|
+
"numpy>=2.4.0",
|
|
13
|
+
"pydantic>=2.12.5",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[tool.hatch.build.targets.wheel]
|
|
17
|
+
packages = ["src/dialograph"]
|
|
18
|
+
|
|
19
|
+
#[tool.uv.extra-build-dependencies]
|
|
20
|
+
cchardet = ["cython"]
|
|
21
|
+
|
|
22
|
+
[dependency-groups]
|
|
23
|
+
dev = [
|
|
24
|
+
"black>=25.12.0",
|
|
25
|
+
"mkdocs>=1.6.1",
|
|
26
|
+
"mkdocs-material>=9.7.1",
|
|
27
|
+
"mkdocstrings[python]>=1.0.0",
|
|
28
|
+
"mypy>=1.19.1",
|
|
29
|
+
"pre-commit>=4.5.1",
|
|
30
|
+
"pytest>=9.0.2",
|
|
31
|
+
"ruff>=0.14.10",
|
|
32
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from langchain_openai import ChatOpenAI
|
|
4
|
+
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate, AIMessagePromptTemplate
|
|
5
|
+
|
|
6
|
+
class Agent:
|
|
7
|
+
def __init__(self, args):
|
|
8
|
+
self.args = args
|
|
9
|
+
self.cost = 0
|
|
10
|
+
|
|
11
|
+
def next_action(self, conversation) -> str:
|
|
12
|
+
raise NotImplementedError("Subclasses must implement this method.")
|
|
13
|
+
|
|
14
|
+
def generate_prompt(self, prompt_template: dict[str, Any]) -> ChatPromptTemplate:
|
|
15
|
+
messages = []
|
|
16
|
+
messages.append(SystemMessagePromptTemplate.from_template(prompt_template["system"]))
|
|
17
|
+
if "assistant" in prompt_template:
|
|
18
|
+
messages.append(AIMessagePromptTemplate.from_template(prompt_template["assistant"]))
|
|
19
|
+
if "user" in prompt_template:
|
|
20
|
+
messages.append(HumanMessagePromptTemplate.from_template(prompt_template["user"]))
|
|
21
|
+
final_prompt_template = ChatPromptTemplate.from_messages(messages)
|
|
22
|
+
return final_prompt_template
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class DialographAgent:
|
|
26
|
+
"""
|
|
27
|
+
Main agent class: proactive, curriculum-aware dialogue.
|
|
28
|
+
Integrates graph memory + temporal dynamics + policy.
|
|
29
|
+
"""
|
|
30
|
+
def __init__(self, args):
|
|
31
|
+
self.data_name = args.data_name
|
|
32
|
+
self.api_key = args.api_key
|
|
33
|
+
self.args = args
|
|
34
|
+
self.mode = args.mode
|
|
35
|
+
self.activate_top_k = args.activate_top_k
|
|
36
|
+
self.activated_memory_nodes = []
|
|
37
|
+
self.recontextualized_guidance = []
|
|
38
|
+
|
|
39
|
+
def next_action(self, conversation) -> str:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def revision(self, conversation) -> str:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
def extract_from_failure(self, conversation) -> str:
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
def extract_from_success(self, conversation) -> str:
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
def retrieve_nodes(self, conversation) -> str:
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
def reinterpretation(self, conversation) -> str:
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
def save_nodes(self, conversation) -> str:
|
|
58
|
+
pass
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Optional, Tuple
|
|
3
|
+
from ..core.node import TemporalNode, MasteryLevel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PedagogicalAction(Enum):
|
|
7
|
+
"""Actions the agent can take"""
|
|
8
|
+
INTRODUCE = "introduce" # First time showing concept
|
|
9
|
+
PRACTICE = "practice" # Reinforce existing concept
|
|
10
|
+
REVIEW = "review" # Remediate forgotten concept
|
|
11
|
+
TEST = "test" # Assess mastery
|
|
12
|
+
WAIT = "wait" # No action needed
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CurriculumPolicy:
|
|
16
|
+
"""
|
|
17
|
+
Decides WHAT to teach and WHEN.
|
|
18
|
+
Core of proactive behavior.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, review_threshold: float = 0.4, mastery_threshold: float = 0.8):
|
|
22
|
+
self.review_threshold = review_threshold
|
|
23
|
+
self.mastery_threshold = mastery_threshold
|
|
24
|
+
|
|
25
|
+
def decide_next_action(
|
|
26
|
+
self,
|
|
27
|
+
knowledge_graph: dict[str, TemporalNode]
|
|
28
|
+
) -> Tuple[PedagogicalAction, Optional[TemporalNode]]:
|
|
29
|
+
"""
|
|
30
|
+
Proactive decision logic.
|
|
31
|
+
Returns: (action, target_node)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
# Priority 1: Review forgotten concepts (urgent)
|
|
35
|
+
for node in knowledge_graph.values():
|
|
36
|
+
if node.mastery == MasteryLevel.NEEDS_REVIEW:
|
|
37
|
+
return PedagogicalAction.REVIEW, node
|
|
38
|
+
|
|
39
|
+
# Priority 2: Practice concepts being learned
|
|
40
|
+
practicing_nodes = [
|
|
41
|
+
n for n in knowledge_graph.values()
|
|
42
|
+
if n.mastery == MasteryLevel.PRACTICING
|
|
43
|
+
]
|
|
44
|
+
if practicing_nodes:
|
|
45
|
+
# Focus on weakest concept
|
|
46
|
+
weakest = min(practicing_nodes, key=lambda n: n.confidence)
|
|
47
|
+
return PedagogicalAction.PRACTICE, weakest
|
|
48
|
+
|
|
49
|
+
# Priority 3: Introduce new concepts (if prerequisites met)
|
|
50
|
+
for node in knowledge_graph.values():
|
|
51
|
+
if node.mastery == MasteryLevel.NOT_SEEN:
|
|
52
|
+
if self._prerequisites_satisfied(node, knowledge_graph):
|
|
53
|
+
return PedagogicalAction.INTRODUCE, node
|
|
54
|
+
|
|
55
|
+
# Priority 4: Test mastered concepts periodically
|
|
56
|
+
mastered = [n for n in knowledge_graph.values()
|
|
57
|
+
if n.mastery == MasteryLevel.MASTERED]
|
|
58
|
+
if mastered and len(mastered) % 5 == 0: # Every 5 mastered concepts
|
|
59
|
+
return PedagogicalAction.TEST, mastered[0]
|
|
60
|
+
|
|
61
|
+
# Nothing to do
|
|
62
|
+
return PedagogicalAction.WAIT, None
|
|
63
|
+
|
|
64
|
+
def _prerequisites_satisfied(
|
|
65
|
+
self,
|
|
66
|
+
node: TemporalNode,
|
|
67
|
+
graph: dict[str, TemporalNode]
|
|
68
|
+
) -> bool:
|
|
69
|
+
"""Check if all prerequisites are mastered"""
|
|
70
|
+
for prereq_id in node.prerequisites:
|
|
71
|
+
prereq = graph.get(prereq_id)
|
|
72
|
+
if not prereq:
|
|
73
|
+
return False
|
|
74
|
+
if prereq.mastery not in [MasteryLevel.MASTERED, MasteryLevel.PRACTICING]:
|
|
75
|
+
return False
|
|
76
|
+
return True
|
|
File without changes
|