gini-toolkit 6.0.1.dev0__py3-none-any.whl
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.
- gini/__init__.py +12 -0
- gini/__main__.py +107 -0
- gini/_version.py +24 -0
- gini/agent/__init__.py +17 -0
- gini/agent/agent_gamemaster.py +140 -0
- gini/agent/api.py +291 -0
- gini/agent/ask.py +123 -0
- gini/agent/authoring.py +72 -0
- gini/agent/blackboard.py +114 -0
- gini/agent/contracts.py +142 -0
- gini/agent/domains.py +91 -0
- gini/agent/embed.py +123 -0
- gini/agent/gamemaster.py +256 -0
- gini/agent/kb.py +148 -0
- gini/agent/lesson_resolver.py +261 -0
- gini/agent/llm/__init__.py +5 -0
- gini/agent/llm/backend.py +43 -0
- gini/agent/llm/fake.py +25 -0
- gini/agent/llm/ollama.py +206 -0
- gini/agent/loop.py +258 -0
- gini/agent/mcp_server.py +86 -0
- gini/agent/meaning.py +225 -0
- gini/agent/mission.py +210 -0
- gini/agent/mission_controller.py +208 -0
- gini/agent/narration.py +116 -0
- gini/agent/notifier.py +86 -0
- gini/agent/personas.py +79 -0
- gini/agent/reasoning.py +172 -0
- gini/agent/recall.py +248 -0
- gini/agent/session.py +79 -0
- gini/agent/teaching_center.py +482 -0
- gini/agent/tools/__init__.py +3 -0
- gini/agent/tools/registry.py +193 -0
- gini/agent/twin/__init__.py +28 -0
- gini/agent/twin/authoring.py +71 -0
- gini/agent/twin/contracts.py +54 -0
- gini/agent/twin/dialectic.py +189 -0
- gini/agent/twin/harness.py +93 -0
- gini/agent/twin/justify.py +156 -0
- gini/agent/twin/learner.py +64 -0
- gini/agent/twin/mission.py +60 -0
- gini/agent/twin/os_coach.py +79 -0
- gini/agent/twin/salience.py +30 -0
- gini/agent/understand.py +250 -0
- gini/agent/verifiers.py +106 -0
- gini/agent/wizard.py +178 -0
- gini/agent/xv6_pack.py +74 -0
- gini/app/__init__.py +3 -0
- gini/app/context.py +368 -0
- gini/app/paths.py +121 -0
- gini/data/README.md +21 -0
- gini/domain/__init__.py +9 -0
- gini/domain/assembly.py +209 -0
- gini/domain/authoring.py +353 -0
- gini/domain/blueprints.py +5 -0
- gini/domain/capabilities.py +177 -0
- gini/domain/catalog.py +85 -0
- gini/domain/certify.py +201 -0
- gini/domain/compose.py +413 -0
- gini/domain/composition.py +88 -0
- gini/domain/concepts.py +383 -0
- gini/domain/connection_rules.py +269 -0
- gini/domain/constraints.py +153 -0
- gini/domain/content.py +59 -0
- gini/domain/cpu_journey.py +89 -0
- gini/domain/devices.py +747 -0
- gini/domain/diagnose.py +201 -0
- gini/domain/element_guide.py +327 -0
- gini/domain/explain.py +90 -0
- gini/domain/fingerprint.py +201 -0
- gini/domain/firewall.py +34 -0
- gini/domain/flowlog.py +61 -0
- gini/domain/flowtable.py +179 -0
- gini/domain/fragment_yaml.py +230 -0
- gini/domain/fragments.py +169 -0
- gini/domain/games/__init__.py +2 -0
- gini/domain/games/paging_games.py +119 -0
- gini/domain/games/policy_game.py +86 -0
- gini/domain/games/process_game.py +48 -0
- gini/domain/games/thrash_game.py +75 -0
- gini/domain/games/translate_game.py +60 -0
- gini/domain/games/trap_game.py +86 -0
- gini/domain/grader.py +155 -0
- gini/domain/grouping.py +67 -0
- gini/domain/legality.py +103 -0
- gini/domain/lesson.py +241 -0
- gini/domain/lexicon.py +150 -0
- gini/domain/machine_state.py +410 -0
- gini/domain/missions/networking/basic-lan.yaml +32 -0
- gini/domain/missions/networking/cache-in-front.yaml +23 -0
- gini/domain/missions/networking/decouple-with-queue.yaml +31 -0
- gini/domain/missions/networking/drive-load.yaml +20 -0
- gini/domain/missions/networking/fix-the-address.yaml +75 -0
- gini/domain/missions/networking/fix-the-lan.yaml +43 -0
- gini/domain/missions/networking/inspect-flows.yaml +16 -0
- gini/domain/missions/networking/k8s-autoscale.yaml +27 -0
- gini/domain/missions/networking/least-privilege.yaml +21 -0
- gini/domain/missions/networking/load-balanced-web.yaml +29 -0
- gini/domain/missions/networking/observe-it.yaml +24 -0
- gini/domain/missions/networking/put-in-vpc.yaml +30 -0
- gini/domain/missions/networking/reachability-boundary.yaml +56 -0
- gini/domain/missions/networking/sdn-reactive.yaml +35 -0
- gini/domain/missions/networking/send-request.yaml +19 -0
- gini/domain/missions/networking/serverless-api.yaml +25 -0
- gini/domain/missions/networking/service-chain.yaml +33 -0
- gini/domain/missions/os/lottery-fix.yaml +19 -0
- gini/domain/missions/os/priority-fix.yaml +24 -0
- gini/domain/missions.py +111 -0
- gini/domain/modulechain.py +36 -0
- gini/domain/objectives.py +488 -0
- gini/domain/os_zoo.py +79 -0
- gini/domain/paging_sim.py +141 -0
- gini/domain/pricing.py +199 -0
- gini/domain/probes.py +226 -0
- gini/domain/profile.py +142 -0
- gini/domain/recipes.py +738 -0
- gini/domain/riders.py +309 -0
- gini/domain/router_modules.py +224 -0
- gini/domain/routetable.py +67 -0
- gini/domain/scoring.py +76 -0
- gini/domain/staging.py +122 -0
- gini/domain/syscall_builder.py +144 -0
- gini/domain/topic_cloud.py +62 -0
- gini/domain/topology.py +213 -0
- gini/domain/vocabulary.py +51 -0
- gini/domain/xv6.py +808 -0
- gini/domain/xv6_fs.py +250 -0
- gini/domain/xv6_runner.py +113 -0
- gini/domain/xv6_vm.py +385 -0
- gini/gloader.py +17 -0
- gini/runtime/__init__.py +18 -0
- gini/runtime/cloudfabric_agent.py +370 -0
- gini/runtime/console.py +68 -0
- gini/runtime/control.py +70 -0
- gini/runtime/frame.py +138 -0
- gini/runtime/gbridge.py +638 -0
- gini/runtime/grouter.py +223 -0
- gini/runtime/hostsim.py +90 -0
- gini/runtime/shuttle.py +348 -0
- gini/runtime/switch.py +109 -0
- gini/runtime/transport.py +77 -0
- gini/runtime/xv6_bridge.py +312 -0
- gini/server/__init__.py +22 -0
- gini/server/__main__.py +74 -0
- gini/server/app.py +140 -0
- gini/server/auth.py +82 -0
- gini/server/policy.py +57 -0
- gini/server/session.py +23 -0
- gini/services/__init__.py +15 -0
- gini/services/boardflash.py +248 -0
- gini/services/boardsetup.py +374 -0
- gini/services/cloud_catalog.py +143 -0
- gini/services/compiler.py +1858 -0
- gini/services/discovery.py +324 -0
- gini/services/gloader.py +183 -0
- gini/services/orchestrator.py +1460 -0
- gini/services/persistence.py +28 -0
- gini/services/probe_runner.py +149 -0
- gini/services/project.py +217 -0
- gini/services/remote.py +93 -0
- gini/services/rider_runner.py +96 -0
- gini/services/rider_session.py +171 -0
- gini/services/shadow_store.py +52 -0
- gini/services/terminal.py +45 -0
- gini/setup/__init__.py +17 -0
- gini/setup/cli.py +109 -0
- gini/setup/images.py +33 -0
- gini/setup/marker.py +43 -0
- gini/setup/runtime.py +69 -0
- gini/ui/__init__.py +3 -0
- gini/ui/assets/app_icon.icns +0 -0
- gini/ui/assets/app_icon.ico +0 -0
- gini/ui/assets/app_icon.png +0 -0
- gini/ui/assets/app_icon_1024.png +0 -0
- gini/ui/assets/cue/_w.txt +1 -0
- gini/ui/assets/cue/ai.png +0 -0
- gini/ui/assets/cue/canvas.png +0 -0
- gini/ui/assets/cue/cloud.png +0 -0
- gini/ui/assets/cue/cost.png +0 -0
- gini/ui/assets/cue/dark/ai.png +0 -0
- gini/ui/assets/cue/dark/canvas.png +0 -0
- gini/ui/assets/cue/dark/cloud.png +0 -0
- gini/ui/assets/cue/dark/cost.png +0 -0
- gini/ui/assets/cue/dark/metrics.png +0 -0
- gini/ui/assets/cue/dark/router.png +0 -0
- gini/ui/assets/cue/dark/run.png +0 -0
- gini/ui/assets/cue/dark/serverless.png +0 -0
- gini/ui/assets/cue/dark/settings.png +0 -0
- gini/ui/assets/cue/dark/welcome.png +0 -0
- gini/ui/assets/cue/dark/wizard.png +0 -0
- gini/ui/assets/cue/ginibrand/ai.png +0 -0
- gini/ui/assets/cue/ginibrand/canvas.png +0 -0
- gini/ui/assets/cue/ginibrand/cloud.png +0 -0
- gini/ui/assets/cue/ginibrand/cost.png +0 -0
- gini/ui/assets/cue/ginibrand/metrics.png +0 -0
- gini/ui/assets/cue/ginibrand/router.png +0 -0
- gini/ui/assets/cue/ginibrand/run.png +0 -0
- gini/ui/assets/cue/ginibrand/serverless.png +0 -0
- gini/ui/assets/cue/ginibrand/settings.png +0 -0
- gini/ui/assets/cue/ginibrand/welcome.png +0 -0
- gini/ui/assets/cue/ginibrand/wizard.png +0 -0
- gini/ui/assets/cue/highcontrast/ai.png +0 -0
- gini/ui/assets/cue/highcontrast/canvas.png +0 -0
- gini/ui/assets/cue/highcontrast/cloud.png +0 -0
- gini/ui/assets/cue/highcontrast/cost.png +0 -0
- gini/ui/assets/cue/highcontrast/metrics.png +0 -0
- gini/ui/assets/cue/highcontrast/router.png +0 -0
- gini/ui/assets/cue/highcontrast/run.png +0 -0
- gini/ui/assets/cue/highcontrast/serverless.png +0 -0
- gini/ui/assets/cue/highcontrast/settings.png +0 -0
- gini/ui/assets/cue/highcontrast/welcome.png +0 -0
- gini/ui/assets/cue/highcontrast/wizard.png +0 -0
- gini/ui/assets/cue/light/ai.png +0 -0
- gini/ui/assets/cue/light/canvas.png +0 -0
- gini/ui/assets/cue/light/cloud.png +0 -0
- gini/ui/assets/cue/light/cost.png +0 -0
- gini/ui/assets/cue/light/metrics.png +0 -0
- gini/ui/assets/cue/light/router.png +0 -0
- gini/ui/assets/cue/light/run.png +0 -0
- gini/ui/assets/cue/light/serverless.png +0 -0
- gini/ui/assets/cue/light/settings.png +0 -0
- gini/ui/assets/cue/light/welcome.png +0 -0
- gini/ui/assets/cue/light/wizard.png +0 -0
- gini/ui/assets/cue/metrics.png +0 -0
- gini/ui/assets/cue/router.png +0 -0
- gini/ui/assets/cue/run.png +0 -0
- gini/ui/assets/cue/serverless.png +0 -0
- gini/ui/assets/cue/settings.png +0 -0
- gini/ui/assets/cue/welcome.png +0 -0
- gini/ui/assets/cue/wizard.png +0 -0
- gini/ui/assistant.py +2111 -0
- gini/ui/author_dialog.py +184 -0
- gini/ui/board_dialog.py +247 -0
- gini/ui/branding.py +21 -0
- gini/ui/canvas.py +2007 -0
- gini/ui/chat_panel.py +7 -0
- gini/ui/cpu_journey.py +212 -0
- gini/ui/cpu_lab.py +306 -0
- gini/ui/cue_cards.py +214 -0
- gini/ui/dashboard.py +222 -0
- gini/ui/diagnose_game.py +336 -0
- gini/ui/fingerprint_lab.py +219 -0
- gini/ui/flash_dialog.py +244 -0
- gini/ui/flow_layout.py +63 -0
- gini/ui/fragment_manager.py +1415 -0
- gini/ui/game_catalog.py +184 -0
- gini/ui/game_renderers.py +340 -0
- gini/ui/games_lab.py +90 -0
- gini/ui/inspector.py +1055 -0
- gini/ui/live_metrics.py +130 -0
- gini/ui/machine_lab.py +1412 -0
- gini/ui/main_window.py +3153 -0
- gini/ui/memory_lab.py +371 -0
- gini/ui/mission_panel.py +302 -0
- gini/ui/mode_indicator.py +227 -0
- gini/ui/palette.py +112 -0
- gini/ui/peripherals.py +218 -0
- gini/ui/process_tree.py +130 -0
- gini/ui/reset_dialog.py +179 -0
- gini/ui/router_lab.py +776 -0
- gini/ui/run_button.py +183 -0
- gini/ui/settings_dialog.py +234 -0
- gini/ui/signin_dialog.py +111 -0
- gini/ui/storage_lab.py +219 -0
- gini/ui/syscall_builder.py +235 -0
- gini/ui/syscall_lab.py +152 -0
- gini/ui/theme/__init__.py +5 -0
- gini/ui/theme/icons.py +145 -0
- gini/ui/theme/manager.py +291 -0
- gini/ui/theme/tokens.py +194 -0
- gini/ui/trap_lab.py +270 -0
- gini/ui/worker_host.py +102 -0
- gini/ui/zoo_lab.py +112 -0
- gini_toolkit-6.0.1.dev0.dist-info/METADATA +77 -0
- gini_toolkit-6.0.1.dev0.dist-info/RECORD +278 -0
- gini_toolkit-6.0.1.dev0.dist-info/WHEEL +5 -0
- gini_toolkit-6.0.1.dev0.dist-info/entry_points.txt +3 -0
- gini_toolkit-6.0.1.dev0.dist-info/top_level.txt +1 -0
gini/domain/profile.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""The student Profile — a transcript of Mission outcomes plus a lightweight mastery model.
|
|
2
|
+
|
|
3
|
+
One profile per student, keyed to identity. In Phase 4 it's a local working copy (JSON on disk);
|
|
4
|
+
Phase 5 makes the Teaching Center its authoritative home and syncs this copy git-style. The data
|
|
5
|
+
is deliberately **append-only / monotonic** (best-band is a max, attempts accumulate, mastery is a
|
|
6
|
+
union), so two machines merge deterministically — the property that makes checkout/checkin work.
|
|
7
|
+
|
|
8
|
+
Contents:
|
|
9
|
+
• transcript — per lesson: best band, attempts used, best (fastest) completing time, when last
|
|
10
|
+
played, and an optional canvas snapshot for audit / server re-grade.
|
|
11
|
+
• mastery — per concept: the best band achieved across lessons that teach it → a coarse level
|
|
12
|
+
(proficient / demonstrated / attempted), which the rest of the agent can use to adapt.
|
|
13
|
+
|
|
14
|
+
Pure data + JSON (de)serialization; no Qt, no network.
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import time
|
|
20
|
+
from dataclasses import asdict, dataclass, field
|
|
21
|
+
|
|
22
|
+
# band ranking (higher is better) — a completed lesson's best_band is a MAX over attempts
|
|
23
|
+
_BAND_RANK = {"": 0, "incomplete": 1, "partial": 2, "pass": 3, "gold": 4}
|
|
24
|
+
# concept mastery level derived from the best band on lessons teaching that concept
|
|
25
|
+
_MASTERY = {4: "proficient", 3: "demonstrated", 2: "attempted", 1: "attempted", 0: "none"}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def band_rank(band: str) -> int:
|
|
29
|
+
return _BAND_RANK.get(band or "", 0)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def better_band(a: str, b: str) -> str:
|
|
33
|
+
return a if band_rank(a) >= band_rank(b) else b
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class LessonRecord:
|
|
38
|
+
lesson_id: str
|
|
39
|
+
concept: str = "" # what the lesson teaches (for mastery)
|
|
40
|
+
best_band: str = ""
|
|
41
|
+
attempts_used: int = 0 # cumulative across sessions
|
|
42
|
+
best_time_s: float | None = None # fastest COMPLETING time (None if never completed)
|
|
43
|
+
completed: bool = False
|
|
44
|
+
last_played: float = 0.0 # epoch seconds
|
|
45
|
+
snapshot: str = "" # optional serialized canvas (audit / re-grade)
|
|
46
|
+
|
|
47
|
+
def merge_outcome(self, band: str, attempts: int, time_s: float | None,
|
|
48
|
+
completed: bool, snapshot: str = "", now: float | None = None) -> None:
|
|
49
|
+
self.best_band = better_band(self.best_band, band)
|
|
50
|
+
self.attempts_used += max(0, attempts)
|
|
51
|
+
self.completed = self.completed or completed
|
|
52
|
+
if completed and time_s is not None:
|
|
53
|
+
self.best_time_s = time_s if self.best_time_s is None else min(self.best_time_s, time_s)
|
|
54
|
+
if snapshot:
|
|
55
|
+
self.snapshot = snapshot
|
|
56
|
+
self.last_played = now if now is not None else time.time()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass
|
|
60
|
+
class Profile:
|
|
61
|
+
student_id: str
|
|
62
|
+
lessons: dict = field(default_factory=dict) # lesson_id -> LessonRecord
|
|
63
|
+
|
|
64
|
+
# -- recording ---------------------------------------------------------- #
|
|
65
|
+
def record(self, lesson, mission, *, snapshot: str = "", now: float | None = None) -> LessonRecord:
|
|
66
|
+
"""Fold a witnessed Mission's outcome into the transcript (monotonic merge)."""
|
|
67
|
+
rec = self.lessons.get(lesson.id)
|
|
68
|
+
if rec is None:
|
|
69
|
+
rec = LessonRecord(lesson_id=lesson.id, concept=getattr(lesson.intent, "concept", ""))
|
|
70
|
+
self.lessons[lesson.id] = rec
|
|
71
|
+
rec.merge_outcome(
|
|
72
|
+
band=mission.last_band or mission.score().band,
|
|
73
|
+
attempts=mission.attempt,
|
|
74
|
+
time_s=mission.elapsed() if mission.complete else None,
|
|
75
|
+
completed=mission.complete, snapshot=snapshot, now=now)
|
|
76
|
+
return rec
|
|
77
|
+
|
|
78
|
+
# -- mastery model ------------------------------------------------------ #
|
|
79
|
+
def mastery(self) -> dict:
|
|
80
|
+
"""concept -> coarse level, from the best band achieved on lessons teaching it."""
|
|
81
|
+
best: dict[str, int] = {}
|
|
82
|
+
for rec in self.lessons.values():
|
|
83
|
+
if rec.concept:
|
|
84
|
+
best[rec.concept] = max(best.get(rec.concept, 0), band_rank(rec.best_band))
|
|
85
|
+
return {c: _MASTERY[r] for c, r in best.items()}
|
|
86
|
+
|
|
87
|
+
def demonstrated_concepts(self) -> list[str]:
|
|
88
|
+
"""Concepts the student has demonstrated (>= pass) — feeds adaptivity."""
|
|
89
|
+
return [c for c, lvl in self.mastery().items() if lvl in ("proficient", "demonstrated")]
|
|
90
|
+
|
|
91
|
+
def summary(self) -> str:
|
|
92
|
+
done = sum(1 for r in self.lessons.values() if r.completed)
|
|
93
|
+
return f"{done}/{len(self.lessons)} lessons completed"
|
|
94
|
+
|
|
95
|
+
# -- persistence (local working copy) ---------------------------------- #
|
|
96
|
+
def to_dict(self) -> dict:
|
|
97
|
+
return {"student_id": self.student_id,
|
|
98
|
+
"lessons": {lid: asdict(rec) for lid, rec in self.lessons.items()}}
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def from_dict(cls, d: dict) -> "Profile":
|
|
102
|
+
p = cls(student_id=d.get("student_id", ""))
|
|
103
|
+
for lid, rd in (d.get("lessons", {}) or {}).items():
|
|
104
|
+
p.lessons[lid] = LessonRecord(**rd)
|
|
105
|
+
return p
|
|
106
|
+
|
|
107
|
+
def save(self, path) -> None:
|
|
108
|
+
from pathlib import Path
|
|
109
|
+
Path(path).parent.mkdir(parents=True, exist_ok=True)
|
|
110
|
+
Path(path).write_text(json.dumps(self.to_dict(), indent=2))
|
|
111
|
+
|
|
112
|
+
@classmethod
|
|
113
|
+
def load(cls, path, *, student_id: str = "") -> "Profile":
|
|
114
|
+
from pathlib import Path
|
|
115
|
+
try:
|
|
116
|
+
return cls.from_dict(json.loads(Path(path).read_text()))
|
|
117
|
+
except (OSError, json.JSONDecodeError):
|
|
118
|
+
return cls(student_id=student_id)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def merge(a: Profile, b: Profile) -> Profile:
|
|
122
|
+
"""Deterministic union merge of two working copies (offline reconciliation). Because the data
|
|
123
|
+
is monotonic, this is well-defined without conflict: best_band = max, attempts = sum of the
|
|
124
|
+
*newer* record's extra, times = min. Here we take the record-wise best/most-complete."""
|
|
125
|
+
out = Profile(student_id=a.student_id or b.student_id)
|
|
126
|
+
for lid in set(a.lessons) | set(b.lessons):
|
|
127
|
+
ra, rb = a.lessons.get(lid), b.lessons.get(lid)
|
|
128
|
+
if ra is None:
|
|
129
|
+
out.lessons[lid] = rb
|
|
130
|
+
elif rb is None:
|
|
131
|
+
out.lessons[lid] = ra
|
|
132
|
+
else:
|
|
133
|
+
out.lessons[lid] = LessonRecord(
|
|
134
|
+
lesson_id=lid, concept=ra.concept or rb.concept,
|
|
135
|
+
best_band=better_band(ra.best_band, rb.best_band),
|
|
136
|
+
attempts_used=max(ra.attempts_used, rb.attempts_used),
|
|
137
|
+
best_time_s=min([t for t in (ra.best_time_s, rb.best_time_s) if t is not None],
|
|
138
|
+
default=None),
|
|
139
|
+
completed=ra.completed or rb.completed,
|
|
140
|
+
last_played=max(ra.last_played, rb.last_played),
|
|
141
|
+
snapshot=ra.snapshot if ra.last_played >= rb.last_played else rb.snapshot)
|
|
142
|
+
return out
|