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)
- .gitignore +0 -0
- .playwright-mcp/page-2026-06-10T14-22-38-882Z.yml +0 -3
- .playwright-mcp/page-2026-06-10T14-27-49-510Z.yml +0 -60
- .playwright-mcp/page-2026-06-10T14-28-44-543Z.yml +0 -67
- .playwright-mcp/page-2026-06-10T14-28-52-588Z.yml +0 -53
- .playwright-mcp/page-2026-06-10T14-29-00-723Z.yml +0 -110
- .playwright-mcp/page-2026-06-10T14-52-32-151Z.yml +0 -3
- .playwright-mcp/page-2026-06-10T14-52-36-986Z.yml +0 -98
- .playwright-mcp/page-2026-06-10T15-12-22-534Z.yml +0 -0
- .playwright-mcp/page-2026-06-10T15-16-39-829Z.yml +0 -3
- .playwright-mcp/page-2026-06-10T15-17-36-795Z.yml +0 -15
- .playwright-mcp/page-2026-06-10T15-18-10-485Z.yml +0 -15
- .playwright-mcp/page-2026-06-10T15-20-12-385Z.yml +0 -3
- .playwright-mcp/page-2026-06-10T23-26-07-697Z.yml +0 -3
- app.py +4 -4
- docs/screenshots/stories/US01-01-alice-home.png +0 -0
- docs/screenshots/stories/US01-02-ask-empty.png +0 -0
- docs/screenshots/stories/US01-03-ask-response.png +0 -0
- docs/screenshots/stories/US01-04-routing-trace.png +0 -0
- docs/screenshots/stories/US02-01-ask-with-rag.png +0 -0
- docs/screenshots/stories/US03-01-chat-tab.png +0 -0
- docs/screenshots/stories/US03-02-chat-sent.png +0 -0
- docs/screenshots/stories/US04-01-mesh-tab-initial.png +0 -0
- docs/screenshots/stories/US04-02-mesh-live-topology.png +0 -0
- docs/screenshots/stories/US04-03-mesh-capability-matrix.png +0 -0
- docs/screenshots/stories/US05-01-settings-identity.png +0 -0
- docs/screenshots/stories/US05-02-settings-peers.png +0 -0
- docs/screenshots/stories/US05-03-settings-join-mesh.png +0 -0
- docs/screenshots/stories/US05-04-settings-specialized-nodes.png +0 -0
- docs/screenshots/stories/US05-05-settings-impl-status.png +0 -0
- docs/screenshots/stories/US06-01-marketplace-tab.png +0 -0
- docs/screenshots/stories/US06-02-marketplace-after-post.png +0 -0
- docs/screenshots/stories/US07-01-files-tab.png +0 -0
- docs/screenshots/stories/US08-01-emergency-tab.png +0 -0
- docs/screenshots/stories/US09-01-bob-home.png +0 -0
- docs/screenshots/stories/US09-02-bob-ask-response.png +0 -0
- docs/screenshots/stories/US09-03-bob-mesh-sees-alice.png +0 -0
- docs/screenshots/stories/US09-04-bob-settings-peers.png +0 -0
- docs/screenshots/stories/US10-01-all-tabs-overview.png +0 -0
- docs/screenshots/stories/US10-02-tab-ask.png +0 -0
- docs/screenshots/stories/US10-02-tab-chat.png +0 -0
- docs/screenshots/stories/US10-02-tab-emergency.png +0 -0
- docs/screenshots/stories/US10-02-tab-files.png +0 -0
- docs/screenshots/stories/US10-02-tab-marketplace.png +0 -0
- docs/screenshots/stories/US10-02-tab-mesh.png +0 -0
- docs/screenshots/stories/US10-02-tab-settings.png +0 -0
- docs/screenshots/stories/US12-01-settings-mesh-connect.png +0 -0
- hearthnet/bus/__init__.py +3 -3
- hearthnet/cli.py +33 -33
- hearthnet/conformance/__init__.py +2 -2
|
Binary files a/.gitignore and b/.gitignore differ
|
|
|
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
- generic [ref=e5]:
|
| 2 |
-
- img [ref=e9]
|
| 3 |
-
- paragraph [ref=e20]: Laden...
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -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]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -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]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -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]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -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]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
- generic [ref=e5]:
|
| 2 |
-
- img [ref=e9]
|
| 3 |
-
- paragraph [ref=e20]: Laden...
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -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]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
File without changes
|
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
- generic [ref=e5]:
|
| 2 |
-
- img [ref=e9]
|
| 3 |
-
- paragraph [ref=e20]: Laden...
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -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]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -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]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
- generic [ref=e5]:
|
| 2 |
-
- img [ref=e9]
|
| 3 |
-
- paragraph [ref=e20]: Laden...
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
- generic [ref=e5]:
|
| 2 |
-
- img [ref=e9]
|
| 3 |
-
- paragraph [ref=e20]: Laden...
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -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 |
-
|
|
|
|
| 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,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -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,
|
| 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:
|
| 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:
|
| 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])
|
|
@@ -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
|
| 652 |
-
|
| 653 |
config = load_config()
|
| 654 |
config_file = get_config_file()
|
| 655 |
-
|
| 656 |
-
click.echo(
|
| 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(
|
| 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(
|
| 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(
|
| 860 |
except Exception:
|
| 861 |
-
click.echo(
|
| 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
|
|
@@ -20,6 +20,6 @@ Usage from CLI:
|
|
| 20 |
|
| 21 |
from __future__ import annotations
|
| 22 |
|
| 23 |
-
from hearthnet.conformance.runner import
|
| 24 |
|
| 25 |
-
__all__ = ["
|
|
|
|
| 20 |
|
| 21 |
from __future__ import annotations
|
| 22 |
|
| 23 |
+
from hearthnet.conformance.runner import ConformanceReport, ConformanceRunner
|
| 24 |
|
| 25 |
+
__all__ = ["ConformanceReport", "ConformanceRunner"]
|