GitHub Actions commited on
Commit
78cc96f
Β·
1 Parent(s): 74c4a03

feat: WebLLM browser agent with PeerJS mesh, HybridRAG, news signals, and easter-egg ticker

Browse files

- webagent/: complete browser-only app (no backend)
- WebLLM ReAct agent with tool-calling (search, scrape, summarize, remember, schedule, RAG)
- PeerJS over-internet mesh (anchor + roster gossip + reclaim failover)
- News RSS ingest with CORS proxy throttling, signal scoring (war/cyber/space/aviation/earth/finance), custom alerts
- HybridRAG integration (localStorage-persisted BM25+phonetic+Levenshtein+n-gram+sentence-window)
- Tab UI (Agent/News/Mesh) with model selector, chat, mesh peers, signal sharing
- Press 'e' to toggle live news ticker overlay (easter-egg)

- Python Phase 1 fixes (unstaged from prior session):
- hearthnet/services/llm/backends/hf_local.py: local Ollama/llama.cpp backend
- hearthnet/services/chat/service.py: chat routing
- hearthnet/services/demo.py: demo service
- app.py: Gradio web UI
- hearthnet/node.py: node initialization
- hearthnet/ui/tabs/ask.py: ask tab UI

Screenshots: 37/38 passed (1 pre-existing rag-trace failure)

This view is limited to 50 files because it contains too many changes. Β  See raw diff
Files changed (50) hide show
  1. .gitignore +0 -0
  2. .playwright-mcp/page-2026-06-10T14-22-38-882Z.yml +0 -3
  3. .playwright-mcp/page-2026-06-10T14-27-49-510Z.yml +0 -60
  4. .playwright-mcp/page-2026-06-10T14-28-44-543Z.yml +0 -67
  5. .playwright-mcp/page-2026-06-10T14-28-52-588Z.yml +0 -53
  6. .playwright-mcp/page-2026-06-10T14-29-00-723Z.yml +0 -110
  7. .playwright-mcp/page-2026-06-10T14-52-32-151Z.yml +0 -3
  8. .playwright-mcp/page-2026-06-10T14-52-36-986Z.yml +0 -98
  9. .playwright-mcp/page-2026-06-10T15-12-22-534Z.yml +0 -0
  10. .playwright-mcp/page-2026-06-10T15-16-39-829Z.yml +0 -3
  11. .playwright-mcp/page-2026-06-10T15-17-36-795Z.yml +0 -15
  12. .playwright-mcp/page-2026-06-10T15-18-10-485Z.yml +0 -15
  13. .playwright-mcp/page-2026-06-10T15-20-12-385Z.yml +0 -3
  14. .playwright-mcp/page-2026-06-10T23-26-07-697Z.yml +0 -3
  15. app.py +4 -4
  16. docs/screenshots/stories/US01-01-alice-home.png +0 -0
  17. docs/screenshots/stories/US01-02-ask-empty.png +0 -0
  18. docs/screenshots/stories/US01-03-ask-response.png +0 -0
  19. docs/screenshots/stories/US01-04-routing-trace.png +0 -0
  20. docs/screenshots/stories/US02-01-ask-with-rag.png +0 -0
  21. docs/screenshots/stories/US03-01-chat-tab.png +0 -0
  22. docs/screenshots/stories/US03-02-chat-sent.png +0 -0
  23. docs/screenshots/stories/US04-01-mesh-tab-initial.png +0 -0
  24. docs/screenshots/stories/US04-02-mesh-live-topology.png +0 -0
  25. docs/screenshots/stories/US04-03-mesh-capability-matrix.png +0 -0
  26. docs/screenshots/stories/US05-01-settings-identity.png +0 -0
  27. docs/screenshots/stories/US05-02-settings-peers.png +0 -0
  28. docs/screenshots/stories/US05-03-settings-join-mesh.png +0 -0
  29. docs/screenshots/stories/US05-04-settings-specialized-nodes.png +0 -0
  30. docs/screenshots/stories/US05-05-settings-impl-status.png +0 -0
  31. docs/screenshots/stories/US06-01-marketplace-tab.png +0 -0
  32. docs/screenshots/stories/US06-02-marketplace-after-post.png +0 -0
  33. docs/screenshots/stories/US07-01-files-tab.png +0 -0
  34. docs/screenshots/stories/US08-01-emergency-tab.png +0 -0
  35. docs/screenshots/stories/US09-01-bob-home.png +0 -0
  36. docs/screenshots/stories/US09-02-bob-ask-response.png +0 -0
  37. docs/screenshots/stories/US09-03-bob-mesh-sees-alice.png +0 -0
  38. docs/screenshots/stories/US09-04-bob-settings-peers.png +0 -0
  39. docs/screenshots/stories/US10-01-all-tabs-overview.png +0 -0
  40. docs/screenshots/stories/US10-02-tab-ask.png +0 -0
  41. docs/screenshots/stories/US10-02-tab-chat.png +0 -0
  42. docs/screenshots/stories/US10-02-tab-emergency.png +0 -0
  43. docs/screenshots/stories/US10-02-tab-files.png +0 -0
  44. docs/screenshots/stories/US10-02-tab-marketplace.png +0 -0
  45. docs/screenshots/stories/US10-02-tab-mesh.png +0 -0
  46. docs/screenshots/stories/US10-02-tab-settings.png +0 -0
  47. docs/screenshots/stories/US12-01-settings-mesh-connect.png +0 -0
  48. hearthnet/bus/__init__.py +3 -3
  49. hearthnet/cli.py +33 -33
  50. hearthnet/conformance/__init__.py +2 -2
.gitignore CHANGED
Binary files a/.gitignore and b/.gitignore differ
 
.playwright-mcp/page-2026-06-10T14-22-38-882Z.yml DELETED
@@ -1,3 +0,0 @@
1
- - generic [ref=e5]:
2
- - img [ref=e9]
3
- - paragraph [ref=e20]: Laden...
 
 
 
 
.playwright-mcp/page-2026-06-10T14-27-49-510Z.yml DELETED
@@ -1,60 +0,0 @@
1
- - generic [ref=e21]:
2
- - main [ref=e22]:
3
- - generic [ref=e23]:
4
- - heading "πŸ”₯ HearthNet β€” Community AI Mesh" [level=1] [ref=e28]
5
- - generic [ref=e29]:
6
- - generic [ref=e32]: ● ONLINE
7
- - paragraph [ref=e37]:
8
- - text: "Node:"
9
- - code [ref=e38]: unknown
10
- - generic [ref=e39]:
11
- - generic [ref=e40]:
12
- - generic [ref=e41]:
13
- - button [ref=e42] [cursor=pointer]: Ask
14
- - button [ref=e43] [cursor=pointer]: Chat
15
- - button [ref=e44] [cursor=pointer]: Marketplace
16
- - button [ref=e45] [cursor=pointer]: Files
17
- - button [ref=e46] [cursor=pointer]: Emergency
18
- - button [ref=e47] [cursor=pointer]: Settings
19
- - tablist [ref=e48]:
20
- - tab "Ask" [ref=e49] [cursor=pointer]
21
- - tab "Chat" [active] [selected] [ref=e50] [cursor=pointer]
22
- - tab "Marketplace" [ref=e51] [cursor=pointer]
23
- - tab "Files" [ref=e52] [cursor=pointer]
24
- - tab "Emergency" [ref=e53] [cursor=pointer]
25
- - tab "Settings" [ref=e54] [cursor=pointer]
26
- - tabpanel [ref=e106]:
27
- - generic [ref=e108]:
28
- - heading "Direct Messages" [level=3] [ref=e113]
29
- - generic [ref=e114]:
30
- - generic [ref=e117]:
31
- - generic [ref=e118]: Recipient Node ID
32
- - textbox "Recipient Node ID" [ref=e120]:
33
- - /placeholder: ed25519:...
34
- - button "Load History" [ref=e121] [cursor=pointer]
35
- - generic [ref=e123]:
36
- - generic:
37
- - generic:
38
- - img
39
- - text: Messages
40
- - log "chatbot conversation" [ref=e124]:
41
- - complementary [ref=e125]
42
- - generic [ref=e126]:
43
- - generic [ref=e129]:
44
- - generic [ref=e130]: Message
45
- - textbox "Message" [ref=e132]:
46
- - /placeholder: ""
47
- - button "Send" [ref=e133] [cursor=pointer]
48
- - contentinfo "Gradio footer navigation" [ref=e97]:
49
- - button "Über API verwenden Logo" [ref=e98] [cursor=pointer]:
50
- - text: Über API verwenden
51
- - img "Logo" [ref=e99]
52
- - generic [ref=e100]: Β·
53
- - link "Mit Gradio erstellt Logo" [ref=e101] [cursor=pointer]:
54
- - /url: https://gradio.app
55
- - text: Mit Gradio erstellt
56
- - img "Logo" [ref=e102]
57
- - generic [ref=e103]: Β·
58
- - button "Einstellungen Einstellungen" [ref=e104] [cursor=pointer]:
59
- - text: Einstellungen
60
- - img "Einstellungen" [ref=e105]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.playwright-mcp/page-2026-06-10T14-28-44-543Z.yml DELETED
@@ -1,67 +0,0 @@
1
- - generic [ref=e21]:
2
- - main [ref=e22]:
3
- - generic [ref=e23]:
4
- - heading "πŸ”₯ HearthNet β€” Community AI Mesh" [level=1] [ref=e28]
5
- - generic [ref=e29]:
6
- - generic [ref=e32]: ● ONLINE
7
- - paragraph [ref=e37]:
8
- - text: "Node:"
9
- - code [ref=e38]: unknown
10
- - generic [ref=e39]:
11
- - generic [ref=e40]:
12
- - generic [ref=e41]:
13
- - button [ref=e42] [cursor=pointer]: Ask
14
- - button [ref=e43] [cursor=pointer]: Chat
15
- - button [ref=e44] [cursor=pointer]: Marketplace
16
- - button [ref=e45] [cursor=pointer]: Files
17
- - button [ref=e46] [cursor=pointer]: Emergency
18
- - button [ref=e47] [cursor=pointer]: Settings
19
- - tablist [ref=e48]:
20
- - tab "Ask" [ref=e49] [cursor=pointer]
21
- - tab "Chat" [ref=e50] [cursor=pointer]
22
- - tab "Marketplace" [active] [selected] [ref=e51] [cursor=pointer]
23
- - tab "Files" [ref=e52] [cursor=pointer]
24
- - tab "Emergency" [ref=e53] [cursor=pointer]
25
- - tab "Settings" [ref=e54] [cursor=pointer]
26
- - tabpanel [ref=e134]:
27
- - generic [ref=e136]:
28
- - heading "Community Marketplace" [level=3] [ref=e141]
29
- - button "πŸ”„ Refresh" [ref=e142] [cursor=pointer]
30
- - generic [ref=e143]:
31
- - generic [ref=e144]:
32
- - generic:
33
- - generic:
34
- - img
35
- - text: Active Posts
36
- - generic "Empty value" [ref=e146]:
37
- - img [ref=e148]
38
- - heading "Post Something" [level=4] [ref=e154]
39
- - generic [ref=e156]:
40
- - generic [ref=e158]:
41
- - generic [ref=e159]: Title
42
- - textbox "Title" [ref=e161]:
43
- - /placeholder: ""
44
- - generic [ref=e163]:
45
- - generic [ref=e164]: Category
46
- - generic [ref=e167]:
47
- - listbox "Category" [ref=e168]: info
48
- - generic:
49
- - img
50
- - generic [ref=e171]:
51
- - generic [ref=e172]: Description
52
- - textbox "Description" [ref=e174]:
53
- - /placeholder: ""
54
- - button "Post" [ref=e175] [cursor=pointer]
55
- - contentinfo "Gradio footer navigation" [ref=e97]:
56
- - button "Über API verwenden Logo" [ref=e98] [cursor=pointer]:
57
- - text: Über API verwenden
58
- - img "Logo" [ref=e99]
59
- - generic [ref=e100]: Β·
60
- - link "Mit Gradio erstellt Logo" [ref=e101] [cursor=pointer]:
61
- - /url: https://gradio.app
62
- - text: Mit Gradio erstellt
63
- - img "Logo" [ref=e102]
64
- - generic [ref=e103]: Β·
65
- - button "Einstellungen Einstellungen" [ref=e104] [cursor=pointer]:
66
- - text: Einstellungen
67
- - img "Einstellungen" [ref=e105]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.playwright-mcp/page-2026-06-10T14-28-52-588Z.yml DELETED
@@ -1,53 +0,0 @@
1
- - generic [ref=e21]:
2
- - main [ref=e22]:
3
- - generic [ref=e23]:
4
- - heading "πŸ”₯ HearthNet β€” Community AI Mesh" [level=1] [ref=e28]
5
- - generic [ref=e29]:
6
- - generic [ref=e32]: ● ONLINE
7
- - paragraph [ref=e37]:
8
- - text: "Node:"
9
- - code [ref=e38]: unknown
10
- - generic [ref=e39]:
11
- - generic [ref=e40]:
12
- - generic [ref=e41]:
13
- - button [ref=e42] [cursor=pointer]: Ask
14
- - button [ref=e43] [cursor=pointer]: Chat
15
- - button [ref=e44] [cursor=pointer]: Marketplace
16
- - button [ref=e45] [cursor=pointer]: Files
17
- - button [ref=e46] [cursor=pointer]: Emergency
18
- - button [ref=e47] [cursor=pointer]: Settings
19
- - tablist [ref=e48]:
20
- - tab "Ask" [ref=e49] [cursor=pointer]
21
- - tab "Chat" [ref=e50] [cursor=pointer]
22
- - tab "Marketplace" [ref=e51] [cursor=pointer]
23
- - tab "Files" [ref=e52] [cursor=pointer]
24
- - tab "Emergency" [active] [selected] [ref=e53] [cursor=pointer]
25
- - tab "Settings" [ref=e54] [cursor=pointer]
26
- - tabpanel [ref=e176]:
27
- - generic [ref=e178]:
28
- - heading "🚨 Emergency Mode" [level=3] [ref=e183]
29
- - generic [ref=e184]:
30
- - generic [ref=e185]:
31
- - generic:
32
- - generic:
33
- - img
34
- - text: Current Mode
35
- - generic "Empty value" [ref=e187]:
36
- - img [ref=e189]
37
- - button "Check Status" [ref=e191] [cursor=pointer]
38
- - heading "Local Resources" [level=4] [ref=e196]
39
- - paragraph [ref=e201]: In offline mode, all capabilities route to local nodes only.
40
- - button "Run Connectivity Probe" [ref=e203] [cursor=pointer]
41
- - contentinfo "Gradio footer navigation" [ref=e97]:
42
- - button "Über API verwenden Logo" [ref=e98] [cursor=pointer]:
43
- - text: Über API verwenden
44
- - img "Logo" [ref=e99]
45
- - generic [ref=e100]: Β·
46
- - link "Mit Gradio erstellt Logo" [ref=e101] [cursor=pointer]:
47
- - /url: https://gradio.app
48
- - text: Mit Gradio erstellt
49
- - img "Logo" [ref=e102]
50
- - generic [ref=e103]: Β·
51
- - button "Einstellungen Einstellungen" [ref=e104] [cursor=pointer]:
52
- - text: Einstellungen
53
- - img "Einstellungen" [ref=e105]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.playwright-mcp/page-2026-06-10T14-29-00-723Z.yml DELETED
@@ -1,110 +0,0 @@
1
- - generic [ref=e21]:
2
- - main [ref=e22]:
3
- - generic [ref=e23]:
4
- - heading "πŸ”₯ HearthNet β€” Community AI Mesh" [level=1] [ref=e28]
5
- - generic [ref=e29]:
6
- - generic [ref=e32]: ● ONLINE
7
- - paragraph [ref=e37]:
8
- - text: "Node:"
9
- - code [ref=e38]: unknown
10
- - generic [ref=e39]:
11
- - generic [ref=e40]:
12
- - generic [ref=e41]:
13
- - button [ref=e42] [cursor=pointer]: Ask
14
- - button [ref=e43] [cursor=pointer]: Chat
15
- - button [ref=e44] [cursor=pointer]: Marketplace
16
- - button [ref=e45] [cursor=pointer]: Files
17
- - button [ref=e46] [cursor=pointer]: Emergency
18
- - button [ref=e47] [cursor=pointer]: Settings
19
- - tablist [ref=e48]:
20
- - tab "Ask" [ref=e49] [cursor=pointer]
21
- - tab "Chat" [ref=e50] [cursor=pointer]
22
- - tab "Marketplace" [ref=e51] [cursor=pointer]
23
- - tab "Files" [ref=e52] [cursor=pointer]
24
- - tab "Emergency" [ref=e53] [cursor=pointer]
25
- - tab "Settings" [active] [selected] [ref=e54] [cursor=pointer]
26
- - tabpanel [ref=e204]:
27
- - generic [ref=e206]:
28
- - heading "Settings" [level=3] [ref=e211]
29
- - heading "Node Identity" [level=4] [ref=e216]
30
- - paragraph [ref=e221]:
31
- - text: "Node ID:"
32
- - code [ref=e222]: not initialized
33
- - paragraph [ref=e227]:
34
- - text: "Profile:"
35
- - code [ref=e228]: hearth
36
- - heading "Community" [level=4] [ref=e233]
37
- - paragraph [ref=e238]:
38
- - text: "Community:"
39
- - code [ref=e239]: none
40
- - heading "Phase Labels" [level=4] [ref=e244]
41
- - table [ref=e249]:
42
- - rowgroup [ref=e250]:
43
- - row "Module Status" [ref=e251]:
44
- - columnheader "Module" [ref=e252]
45
- - columnheader "Status" [ref=e253]
46
- - rowgroup [ref=e254]:
47
- - row "M01 Identity βœ… Implemented" [ref=e255]:
48
- - cell "M01 Identity" [ref=e256]
49
- - cell "βœ… Implemented" [ref=e257]
50
- - row "M02 Discovery βœ… Implemented (mDNS/UDP)" [ref=e258]:
51
- - cell "M02 Discovery" [ref=e259]
52
- - cell "βœ… Implemented (mDNS/UDP)" [ref=e260]
53
- - row "M03 Bus βœ… Implemented" [ref=e261]:
54
- - cell "M03 Bus" [ref=e262]
55
- - cell "βœ… Implemented" [ref=e263]
56
- - row "M04 LLM βœ… Implemented (Ollama/llama.cpp/HF)" [ref=e264]:
57
- - cell "M04 LLM" [ref=e265]
58
- - cell "βœ… Implemented (Ollama/llama.cpp/HF)" [ref=e266]
59
- - row "M05 RAG βœ… Implemented" [ref=e267]:
60
- - cell "M05 RAG" [ref=e268]
61
- - cell "βœ… Implemented" [ref=e269]
62
- - row "M06 Marketplace βœ… Implemented (event-sourced)" [ref=e270]:
63
- - cell "M06 Marketplace" [ref=e271]
64
- - cell "βœ… Implemented (event-sourced)" [ref=e272]
65
- - row "M07 Blobs βœ… Implemented" [ref=e273]:
66
- - cell "M07 Blobs" [ref=e274]
67
- - cell "βœ… Implemented" [ref=e275]
68
- - row "M08 UI βœ… This UI" [ref=e276]:
69
- - cell "M08 UI" [ref=e277]
70
- - cell "βœ… This UI" [ref=e278]
71
- - row "M09 Emergency βœ… Implemented" [ref=e279]:
72
- - cell "M09 Emergency" [ref=e280]
73
- - cell "βœ… Implemented" [ref=e281]
74
- - row "M10 Chat βœ… Implemented" [ref=e282]:
75
- - cell "M10 Chat" [ref=e283]
76
- - cell "βœ… Implemented" [ref=e284]
77
- - row "M11 Embedding βœ… Implemented" [ref=e285]:
78
- - cell "M11 Embedding" [ref=e286]
79
- - cell "βœ… Implemented" [ref=e287]
80
- - row "M12 CLI βœ… Implemented" [ref=e288]:
81
- - cell "M12 CLI" [ref=e289]
82
- - cell "βœ… Implemented" [ref=e290]
83
- - row "M13 Onboarding βœ… Implemented" [ref=e291]:
84
- - cell "M13 Onboarding" [ref=e292]
85
- - cell "βœ… Implemented" [ref=e293]
86
- - row "X01 Transport βœ… Implemented (FastAPI)" [ref=e294]:
87
- - cell "X01 Transport" [ref=e295]
88
- - cell "βœ… Implemented (FastAPI)" [ref=e296]
89
- - row "X02 Events βœ… Implemented (SQLite)" [ref=e297]:
90
- - cell "X02 Events" [ref=e298]
91
- - cell "βœ… Implemented (SQLite)" [ref=e299]
92
- - row "X03 Observability βœ… Implemented" [ref=e300]:
93
- - cell "X03 Observability" [ref=e301]
94
- - cell "βœ… Implemented" [ref=e302]
95
- - row "X04 Config βœ… Implemented" [ref=e303]:
96
- - cell "X04 Config" [ref=e304]
97
- - cell "βœ… Implemented" [ref=e305]
98
- - contentinfo "Gradio footer navigation" [ref=e97]:
99
- - button "Über API verwenden Logo" [ref=e98] [cursor=pointer]:
100
- - text: Über API verwenden
101
- - img "Logo" [ref=e99]
102
- - generic [ref=e100]: Β·
103
- - link "Mit Gradio erstellt Logo" [ref=e101] [cursor=pointer]:
104
- - /url: https://gradio.app
105
- - text: Mit Gradio erstellt
106
- - img "Logo" [ref=e102]
107
- - generic [ref=e103]: Β·
108
- - button "Einstellungen Einstellungen" [ref=e104] [cursor=pointer]:
109
- - text: Einstellungen
110
- - img "Einstellungen" [ref=e105]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.playwright-mcp/page-2026-06-10T14-52-32-151Z.yml DELETED
@@ -1,3 +0,0 @@
1
- - generic [ref=e5]:
2
- - img [ref=e9]
3
- - paragraph [ref=e20]: Laden...
 
 
 
 
.playwright-mcp/page-2026-06-10T14-52-36-986Z.yml DELETED
@@ -1,98 +0,0 @@
1
- - generic [ref=e21]:
2
- - main [ref=e22]:
3
- - generic [ref=e23]:
4
- - heading "πŸ”₯ HearthNet β€” Community AI Mesh" [level=1] [ref=e28]
5
- - generic [ref=e29]:
6
- - generic [ref=e32]: ● ONLINE
7
- - paragraph [ref=e37]:
8
- - text: "Node:"
9
- - code [ref=e38]: unknown
10
- - generic [ref=e39]:
11
- - generic [ref=e40]:
12
- - generic [ref=e41]:
13
- - button [ref=e42] [cursor=pointer]: Ask
14
- - button [ref=e43] [cursor=pointer]: Chat
15
- - button [ref=e44] [cursor=pointer]: Marketplace
16
- - button [ref=e45] [cursor=pointer]: Files
17
- - button [ref=e46] [cursor=pointer]: Emergency
18
- - button [ref=e47] [cursor=pointer]: Settings
19
- - tablist [ref=e48]:
20
- - tab "Ask" [ref=e49] [cursor=pointer]
21
- - tab "Chat" [ref=e50] [cursor=pointer]
22
- - tab "Marketplace" [ref=e51] [cursor=pointer]
23
- - tab "Files" [ref=e52] [cursor=pointer]
24
- - tab "Emergency" [ref=e53] [cursor=pointer]
25
- - tab "Settings" [active] [selected] [ref=e54] [cursor=pointer]
26
- - tabpanel [ref=e55]:
27
- - generic [ref=e57]:
28
- - heading "βš™οΈ Node & Settings" [level=3] [ref=e62]
29
- - generic [ref=e63]:
30
- - button "πŸͺͺ Node Identity β–Ό" [ref=e64] [cursor=pointer]:
31
- - generic [ref=e65]: πŸͺͺ Node Identity
32
- - generic [ref=e66]: β–Ό
33
- - table [ref=e73]:
34
- - rowgroup [ref=e74]:
35
- - row "Field Value" [ref=e75]:
36
- - columnheader "Field" [ref=e76]
37
- - columnheader "Value" [ref=e77]
38
- - rowgroup [ref=e78]:
39
- - row "Node ID not initialized" [ref=e79]:
40
- - cell "Node ID" [ref=e80]
41
- - cell "not initialized" [ref=e81]:
42
- - code [ref=e82]: not initialized
43
- - row "Profile hearth" [ref=e83]:
44
- - cell "Profile" [ref=e84]
45
- - cell "hearth" [ref=e85]:
46
- - code [ref=e86]: hearth
47
- - row "Community none" [ref=e87]:
48
- - cell "Community" [ref=e88]
49
- - cell "none" [ref=e89]:
50
- - code [ref=e90]: none
51
- - generic [ref=e91]:
52
- - button "🌐 Connected Peers & Capabilities β–Ό" [ref=e92] [cursor=pointer]:
53
- - generic [ref=e93]: 🌐 Connected Peers & Capabilities
54
- - generic [ref=e94]: β–Ό
55
- - generic [ref=e96]:
56
- - generic [ref=e97]:
57
- - generic [ref=e98]:
58
- - generic:
59
- - generic:
60
- - img
61
- - text: Peers
62
- - button "Copy" [ref=e100] [cursor=pointer]:
63
- - img [ref=e102]
64
- - generic [ref=e106]:
65
- - generic [ref=e107]:
66
- - generic "Line number 1" [ref=e108]: "1"
67
- - generic [ref=e109]:
68
- - button "Collapse" [ref=e110] [cursor=pointer]: β–Ό
69
- - generic [ref=e111]: "["
70
- - generic [ref=e113]:
71
- - generic "Line number 2" [ref=e114]: "2"
72
- - generic [ref=e116]: "]"
73
- - button "πŸ”„ Refresh Peers" [ref=e117] [cursor=pointer]
74
- - button "πŸ“¨ Invite a Node β–Ό" [ref=e119] [cursor=pointer]:
75
- - generic [ref=e120]: πŸ“¨ Invite a Node
76
- - generic [ref=e121]: β–Ό
77
- - button "πŸ“š RAG β€” Ingest Documents β–Ό" [ref=e123] [cursor=pointer]:
78
- - generic [ref=e124]: πŸ“š RAG β€” Ingest Documents
79
- - generic [ref=e125]: β–Ό
80
- - button "πŸ“‹ Configuration Overview β–Ό" [ref=e127] [cursor=pointer]:
81
- - generic [ref=e128]: πŸ“‹ Configuration Overview
82
- - generic [ref=e129]: β–Ό
83
- - button "πŸ”¬ Implementation Status β–Ό" [ref=e131] [cursor=pointer]:
84
- - generic [ref=e132]: πŸ”¬ Implementation Status
85
- - generic [ref=e133]: β–Ό
86
- - contentinfo "Gradio footer navigation" [ref=e134]:
87
- - button "Über API verwenden Logo" [ref=e135] [cursor=pointer]:
88
- - text: Über API verwenden
89
- - img "Logo" [ref=e136]
90
- - generic [ref=e137]: Β·
91
- - link "Mit Gradio erstellt Logo" [ref=e138] [cursor=pointer]:
92
- - /url: https://gradio.app
93
- - text: Mit Gradio erstellt
94
- - img "Logo" [ref=e139]
95
- - generic [ref=e140]: Β·
96
- - button "Einstellungen Einstellungen" [ref=e141] [cursor=pointer]:
97
- - text: Einstellungen
98
- - img "Einstellungen" [ref=e142]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.playwright-mcp/page-2026-06-10T15-12-22-534Z.yml DELETED
File without changes
.playwright-mcp/page-2026-06-10T15-16-39-829Z.yml DELETED
@@ -1,3 +0,0 @@
1
- - generic [ref=e5]:
2
- - img [ref=e9]
3
- - paragraph [ref=e20]: Laden...
 
 
 
 
.playwright-mcp/page-2026-06-10T15-17-36-795Z.yml DELETED
@@ -1,15 +0,0 @@
1
- - generic [ref=e5]:
2
- - main [ref=e6]
3
- - contentinfo "Gradio footer navigation" [ref=e7]:
4
- - button "Über API verwenden Logo" [ref=e8] [cursor=pointer]:
5
- - text: Über API verwenden
6
- - img "Logo" [ref=e9]
7
- - generic [ref=e10]: Β·
8
- - link "Mit Gradio erstellt Logo" [ref=e11] [cursor=pointer]:
9
- - /url: https://gradio.app
10
- - text: Mit Gradio erstellt
11
- - img "Logo" [ref=e12]
12
- - generic [ref=e13]: Β·
13
- - button "Einstellungen Einstellungen" [ref=e14] [cursor=pointer]:
14
- - text: Einstellungen
15
- - img "Einstellungen" [ref=e15]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.playwright-mcp/page-2026-06-10T15-18-10-485Z.yml DELETED
@@ -1,15 +0,0 @@
1
- - generic [ref=e5]:
2
- - main [ref=e6]
3
- - contentinfo "Gradio footer navigation" [ref=e7]:
4
- - button "Über API verwenden Logo" [ref=e8] [cursor=pointer]:
5
- - text: Über API verwenden
6
- - img "Logo" [ref=e9]
7
- - generic [ref=e10]: Β·
8
- - link "Mit Gradio erstellt Logo" [ref=e11] [cursor=pointer]:
9
- - /url: https://gradio.app
10
- - text: Mit Gradio erstellt
11
- - img "Logo" [ref=e12]
12
- - generic [ref=e13]: Β·
13
- - button "Einstellungen Einstellungen" [ref=e14] [cursor=pointer]:
14
- - text: Einstellungen
15
- - img "Einstellungen" [ref=e15]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.playwright-mcp/page-2026-06-10T15-20-12-385Z.yml DELETED
@@ -1,3 +0,0 @@
1
- - generic [ref=e5]:
2
- - img [ref=e9]
3
- - paragraph [ref=e20]: Laden...
 
 
 
 
.playwright-mcp/page-2026-06-10T23-26-07-697Z.yml DELETED
@@ -1,3 +0,0 @@
1
- - generic [ref=e5]:
2
- - img [ref=e9]
3
- - paragraph [ref=e20]: Laden...
 
 
 
 
app.py CHANGED
@@ -197,6 +197,7 @@ def _build_node():
197
  import time as _time
198
 
199
  from hearthnet.services.llm.backends.base import ChatResult
 
200
 
201
  @_spaces.GPU(duration=120)
202
  def _gpu_pipeline_call(
@@ -230,9 +231,7 @@ def _build_node():
230
  if self._pipeline is None:
231
  raise RuntimeError("HF model not loaded")
232
  t0 = _time.monotonic()
233
- prompt = (
234
- "\n".join(f"{m['role']}: {m['content']}" for m in messages) + "\nassistant:"
235
- )
236
  loop = asyncio.get_running_loop()
237
  result = await loop.run_in_executor(
238
  None,
@@ -240,7 +239,8 @@ def _build_node():
240
  self._pipeline, prompt, max_tokens, temperature
241
  ),
242
  )
243
- text = result[0]["generated_text"] if result else ""
 
244
  ms = int((_time.monotonic() - t0) * 1000)
245
  return ChatResult(
246
  text=text,
 
197
  import time as _time
198
 
199
  from hearthnet.services.llm.backends.base import ChatResult
200
+ from hearthnet.services.llm.backends.hf_local import _trim_generated
201
 
202
  @_spaces.GPU(duration=120)
203
  def _gpu_pipeline_call(
 
231
  if self._pipeline is None:
232
  raise RuntimeError("HF model not loaded")
233
  t0 = _time.monotonic()
234
+ prompt = self._build_prompt(messages)
 
 
235
  loop = asyncio.get_running_loop()
236
  result = await loop.run_in_executor(
237
  None,
 
239
  self._pipeline, prompt, max_tokens, temperature
240
  ),
241
  )
242
+ raw = result[0]["generated_text"] if result else ""
243
+ text = _trim_generated(raw)
244
  ms = int((_time.monotonic() - t0) * 1000)
245
  return ChatResult(
246
  text=text,
docs/screenshots/stories/US01-01-alice-home.png CHANGED
docs/screenshots/stories/US01-02-ask-empty.png CHANGED
docs/screenshots/stories/US01-03-ask-response.png CHANGED
docs/screenshots/stories/US01-04-routing-trace.png CHANGED
docs/screenshots/stories/US02-01-ask-with-rag.png CHANGED
docs/screenshots/stories/US03-01-chat-tab.png CHANGED
docs/screenshots/stories/US03-02-chat-sent.png CHANGED
docs/screenshots/stories/US04-01-mesh-tab-initial.png CHANGED
docs/screenshots/stories/US04-02-mesh-live-topology.png CHANGED
docs/screenshots/stories/US04-03-mesh-capability-matrix.png CHANGED
docs/screenshots/stories/US05-01-settings-identity.png CHANGED
docs/screenshots/stories/US05-02-settings-peers.png CHANGED
docs/screenshots/stories/US05-03-settings-join-mesh.png CHANGED
docs/screenshots/stories/US05-04-settings-specialized-nodes.png CHANGED
docs/screenshots/stories/US05-05-settings-impl-status.png CHANGED
docs/screenshots/stories/US06-01-marketplace-tab.png CHANGED
docs/screenshots/stories/US06-02-marketplace-after-post.png CHANGED
docs/screenshots/stories/US07-01-files-tab.png CHANGED
docs/screenshots/stories/US08-01-emergency-tab.png CHANGED
docs/screenshots/stories/US09-01-bob-home.png CHANGED
docs/screenshots/stories/US09-02-bob-ask-response.png CHANGED
docs/screenshots/stories/US09-03-bob-mesh-sees-alice.png CHANGED
docs/screenshots/stories/US09-04-bob-settings-peers.png CHANGED
docs/screenshots/stories/US10-01-all-tabs-overview.png CHANGED
docs/screenshots/stories/US10-02-tab-ask.png CHANGED
docs/screenshots/stories/US10-02-tab-chat.png CHANGED
docs/screenshots/stories/US10-02-tab-emergency.png CHANGED
docs/screenshots/stories/US10-02-tab-files.png CHANGED
docs/screenshots/stories/US10-02-tab-marketplace.png CHANGED
docs/screenshots/stories/US10-02-tab-mesh.png CHANGED
docs/screenshots/stories/US10-02-tab-settings.png CHANGED
docs/screenshots/stories/US12-01-settings-mesh-connect.png ADDED
hearthnet/bus/__init__.py CHANGED
@@ -13,7 +13,7 @@ from hearthnet.bus.capability import (
13
  RouteRequest,
14
  )
15
  from hearthnet.bus.health import HealthTracker
16
- from hearthnet.bus.registry import Diff, RegistryEvent, Registry
17
  from hearthnet.bus.router import BusConfig, Router
18
  from hearthnet.types import CapabilityName, HearthNetError, Version
19
 
@@ -211,7 +211,7 @@ class CapabilityBus:
211
  elapsed = (time.monotonic() - started) * 1000
212
  self.health.record(entry, success=True, latency_ms=elapsed)
213
  return (entry.node_id, result)
214
- except Exception as exc: # noqa: BLE001 β€” fan-out tolerates partial failure
215
  elapsed = (time.monotonic() - started) * 1000
216
  self.health.record(entry, success=False, latency_ms=elapsed)
217
  self._traces.append(
@@ -231,7 +231,7 @@ class CapabilityBus:
231
  async def _guarded(entry: CapabilityEntry) -> tuple[str, dict[str, Any]] | None:
232
  try:
233
  return await asyncio.wait_for(_invoke(entry), timeout=timeout_seconds)
234
- except Exception: # noqa: BLE001
235
  return None
236
 
237
  gathered = await asyncio.gather(*[_guarded(e) for e in entries])
 
13
  RouteRequest,
14
  )
15
  from hearthnet.bus.health import HealthTracker
16
+ from hearthnet.bus.registry import Diff, Registry, RegistryEvent
17
  from hearthnet.bus.router import BusConfig, Router
18
  from hearthnet.types import CapabilityName, HearthNetError, Version
19
 
 
211
  elapsed = (time.monotonic() - started) * 1000
212
  self.health.record(entry, success=True, latency_ms=elapsed)
213
  return (entry.node_id, result)
214
+ except Exception as exc:
215
  elapsed = (time.monotonic() - started) * 1000
216
  self.health.record(entry, success=False, latency_ms=elapsed)
217
  self._traces.append(
 
231
  async def _guarded(entry: CapabilityEntry) -> tuple[str, dict[str, Any]] | None:
232
  try:
233
  return await asyncio.wait_for(_invoke(entry), timeout=timeout_seconds)
234
+ except Exception:
235
  return None
236
 
237
  gathered = await asyncio.gather(*[_guarded(e) for e in entries])
hearthnet/cli.py CHANGED
@@ -648,15 +648,15 @@ def config() -> None:
648
  def config_show() -> None:
649
  """Display current HearthNet configuration."""
650
  try:
651
- from build.shared.first_run import load_config, get_config_file
652
-
653
  config = load_config()
654
  config_file = get_config_file()
655
-
656
- click.echo(f"πŸ“‹ HearthNet Configuration")
657
  click.echo(f"Location: {config_file}")
658
  click.echo("")
659
-
660
  for key, value in config.items():
661
  if isinstance(value, bool):
662
  value_str = "βœ… Yes" if value else "❌ No"
@@ -675,9 +675,9 @@ def config_set(key: str, value: str) -> None:
675
  """Update a configuration value."""
676
  try:
677
  from build.shared.first_run import load_config, save_config
678
-
679
  config = load_config()
680
-
681
  # Type conversion
682
  if value.lower() in ("true", "yes", "1"):
683
  config[key] = True
@@ -687,7 +687,7 @@ def config_set(key: str, value: str) -> None:
687
  config[key] = int(value)
688
  else:
689
  config[key] = value
690
-
691
  if save_config(config):
692
  click.echo(f"βœ… Config updated: {key} = {config[key]}")
693
  else:
@@ -714,21 +714,21 @@ def model_download(model_id: str, cache: str | None) -> None:
714
  """Download and cache an LLM model from HuggingFace Hub."""
715
  try:
716
  from build.shared.download_model import download_model, get_model_path, is_model_cached
717
-
718
  if is_model_cached(model_id):
719
  click.echo(f"βœ… Model already cached: {get_model_path(model_id)}")
720
  return
721
-
722
  click.echo(f"πŸ“₯ Downloading model: {model_id}")
723
  click.echo(" (This may take several minutes depending on model size)")
724
-
725
  success = download_model(model_id, destination=Path(cache) if cache else None)
726
-
727
  if success:
728
  model_path = get_model_path(model_id)
729
  click.echo(f"βœ… Model downloaded and cached at: {model_path}")
730
  else:
731
- click.echo(f"❌ Failed to download model", err=True)
732
  sys.exit(1)
733
  except Exception as exc:
734
  click.echo(f"❌ Error: {exc}", err=True)
@@ -740,27 +740,27 @@ def model_list() -> None:
740
  """List cached models."""
741
  try:
742
  from build.shared.download_model import get_model_cache_dir
743
-
744
  cache_dir = get_model_cache_dir()
745
-
746
  if not cache_dir.exists() or not list(cache_dir.iterdir()):
747
  click.echo("πŸ“¦ No cached models found.")
748
  click.echo(f" Cache location: {cache_dir}")
749
  return
750
-
751
  click.echo("πŸ“¦ Cached Models:")
752
  click.echo("")
753
-
754
  for model_dir in sorted(cache_dir.iterdir()):
755
  if not model_dir.is_dir():
756
  continue
757
-
758
  size_mb = sum(
759
  f.stat().st_size for f in model_dir.rglob("*") if f.is_file()
760
  ) / (1024 * 1024)
761
-
762
  file_count = len(list(model_dir.rglob("*")))
763
-
764
  click.echo(f" πŸ“ {model_dir.name}")
765
  click.echo(f" Size: {size_mb:.1f} MB Files: {file_count}")
766
  except Exception as exc:
@@ -774,12 +774,12 @@ def model_info(model_id: str) -> None:
774
  """Get information about a model."""
775
  try:
776
  from build.shared.download_model import get_model_info
777
-
778
  info = get_model_info(model_id)
779
-
780
  click.echo(f"πŸ“Š Model Information: {model_id}")
781
  click.echo("")
782
-
783
  for key, value in info.items():
784
  if key == "size_mb":
785
  click.echo(f" Size: {value:.1f} MB")
@@ -806,7 +806,7 @@ def health(detailed: bool) -> None:
806
  """Quick health check of HearthNet installation."""
807
  checks_passed = 0
808
  checks_failed = 0
809
-
810
  # 1. Python version
811
  import sys
812
  py_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
@@ -816,7 +816,7 @@ def health(detailed: bool) -> None:
816
  else:
817
  click.echo(f"❌ Python: {py_version} (requires 3.12+)")
818
  checks_failed += 1
819
-
820
  # 2. Key dependencies
821
  deps = ["click", "gradio", "transformers", "torch", "fastapi"]
822
  for dep in deps:
@@ -827,15 +827,15 @@ def health(detailed: bool) -> None:
827
  except ImportError:
828
  click.echo(f"❌ {dep}: NOT installed")
829
  checks_failed += 1
830
-
831
  # 3. Model cache
832
  try:
833
  from build.shared.download_model import get_model_cache_dir, is_model_cached
834
  from build.shared.first_run import load_config
835
-
836
  config = load_config()
837
  model_id = config.get("model_id", "HuggingFaceTB/SmolLM2-135M-Instruct")
838
-
839
  if is_model_cached(model_id):
840
  click.echo(f"βœ… Model: {model_id} (cached)")
841
  checks_passed += 1
@@ -845,8 +845,8 @@ def health(detailed: bool) -> None:
845
  cache_dir = get_model_cache_dir()
846
  click.echo(f" Cache location: {cache_dir}")
847
  except Exception:
848
- click.echo(f"⚠️ Model: could not verify")
849
-
850
  # 4. GPU support
851
  try:
852
  import torch
@@ -856,10 +856,10 @@ def health(detailed: bool) -> None:
856
  click.echo(f"βœ… GPU: {gpu_name}")
857
  checks_passed += 1
858
  else:
859
- click.echo(f"ℹ️ GPU: not available (CPU mode)")
860
  except Exception:
861
- click.echo(f"ℹ️ GPU: could not detect")
862
-
863
  # Summary
864
  click.echo("")
865
  total = checks_passed + checks_failed
 
648
  def config_show() -> None:
649
  """Display current HearthNet configuration."""
650
  try:
651
+ from build.shared.first_run import get_config_file, load_config
652
+
653
  config = load_config()
654
  config_file = get_config_file()
655
+
656
+ click.echo("πŸ“‹ HearthNet Configuration")
657
  click.echo(f"Location: {config_file}")
658
  click.echo("")
659
+
660
  for key, value in config.items():
661
  if isinstance(value, bool):
662
  value_str = "βœ… Yes" if value else "❌ No"
 
675
  """Update a configuration value."""
676
  try:
677
  from build.shared.first_run import load_config, save_config
678
+
679
  config = load_config()
680
+
681
  # Type conversion
682
  if value.lower() in ("true", "yes", "1"):
683
  config[key] = True
 
687
  config[key] = int(value)
688
  else:
689
  config[key] = value
690
+
691
  if save_config(config):
692
  click.echo(f"βœ… Config updated: {key} = {config[key]}")
693
  else:
 
714
  """Download and cache an LLM model from HuggingFace Hub."""
715
  try:
716
  from build.shared.download_model import download_model, get_model_path, is_model_cached
717
+
718
  if is_model_cached(model_id):
719
  click.echo(f"βœ… Model already cached: {get_model_path(model_id)}")
720
  return
721
+
722
  click.echo(f"πŸ“₯ Downloading model: {model_id}")
723
  click.echo(" (This may take several minutes depending on model size)")
724
+
725
  success = download_model(model_id, destination=Path(cache) if cache else None)
726
+
727
  if success:
728
  model_path = get_model_path(model_id)
729
  click.echo(f"βœ… Model downloaded and cached at: {model_path}")
730
  else:
731
+ click.echo("❌ Failed to download model", err=True)
732
  sys.exit(1)
733
  except Exception as exc:
734
  click.echo(f"❌ Error: {exc}", err=True)
 
740
  """List cached models."""
741
  try:
742
  from build.shared.download_model import get_model_cache_dir
743
+
744
  cache_dir = get_model_cache_dir()
745
+
746
  if not cache_dir.exists() or not list(cache_dir.iterdir()):
747
  click.echo("πŸ“¦ No cached models found.")
748
  click.echo(f" Cache location: {cache_dir}")
749
  return
750
+
751
  click.echo("πŸ“¦ Cached Models:")
752
  click.echo("")
753
+
754
  for model_dir in sorted(cache_dir.iterdir()):
755
  if not model_dir.is_dir():
756
  continue
757
+
758
  size_mb = sum(
759
  f.stat().st_size for f in model_dir.rglob("*") if f.is_file()
760
  ) / (1024 * 1024)
761
+
762
  file_count = len(list(model_dir.rglob("*")))
763
+
764
  click.echo(f" πŸ“ {model_dir.name}")
765
  click.echo(f" Size: {size_mb:.1f} MB Files: {file_count}")
766
  except Exception as exc:
 
774
  """Get information about a model."""
775
  try:
776
  from build.shared.download_model import get_model_info
777
+
778
  info = get_model_info(model_id)
779
+
780
  click.echo(f"πŸ“Š Model Information: {model_id}")
781
  click.echo("")
782
+
783
  for key, value in info.items():
784
  if key == "size_mb":
785
  click.echo(f" Size: {value:.1f} MB")
 
806
  """Quick health check of HearthNet installation."""
807
  checks_passed = 0
808
  checks_failed = 0
809
+
810
  # 1. Python version
811
  import sys
812
  py_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
 
816
  else:
817
  click.echo(f"❌ Python: {py_version} (requires 3.12+)")
818
  checks_failed += 1
819
+
820
  # 2. Key dependencies
821
  deps = ["click", "gradio", "transformers", "torch", "fastapi"]
822
  for dep in deps:
 
827
  except ImportError:
828
  click.echo(f"❌ {dep}: NOT installed")
829
  checks_failed += 1
830
+
831
  # 3. Model cache
832
  try:
833
  from build.shared.download_model import get_model_cache_dir, is_model_cached
834
  from build.shared.first_run import load_config
835
+
836
  config = load_config()
837
  model_id = config.get("model_id", "HuggingFaceTB/SmolLM2-135M-Instruct")
838
+
839
  if is_model_cached(model_id):
840
  click.echo(f"βœ… Model: {model_id} (cached)")
841
  checks_passed += 1
 
845
  cache_dir = get_model_cache_dir()
846
  click.echo(f" Cache location: {cache_dir}")
847
  except Exception:
848
+ click.echo("⚠️ Model: could not verify")
849
+
850
  # 4. GPU support
851
  try:
852
  import torch
 
856
  click.echo(f"βœ… GPU: {gpu_name}")
857
  checks_passed += 1
858
  else:
859
+ click.echo("ℹ️ GPU: not available (CPU mode)")
860
  except Exception:
861
+ click.echo("ℹ️ GPU: could not detect")
862
+
863
  # Summary
864
  click.echo("")
865
  total = checks_passed + checks_failed
hearthnet/conformance/__init__.py CHANGED
@@ -20,6 +20,6 @@ Usage from CLI:
20
 
21
  from __future__ import annotations
22
 
23
- from hearthnet.conformance.runner import ConformanceRunner, ConformanceReport
24
 
25
- __all__ = ["ConformanceRunner", "ConformanceReport"]
 
20
 
21
  from __future__ import annotations
22
 
23
+ from hearthnet.conformance.runner import ConformanceReport, ConformanceRunner
24
 
25
+ __all__ = ["ConformanceReport", "ConformanceRunner"]