echoact 0.1.0__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.
- echoact/__init__.py +3 -0
- echoact/__main__.py +117 -0
- echoact/app.py +315 -0
- echoact/audio/__init__.py +0 -0
- echoact/audio/devices.py +192 -0
- echoact/audio/player.py +611 -0
- echoact/audio/wav.py +854 -0
- echoact/config/__init__.py +0 -0
- echoact/config/budget.py +370 -0
- echoact/config/settings.py +1244 -0
- echoact/db/__init__.py +0 -0
- echoact/db/backup.py +2429 -0
- echoact/db/migrations.py +434 -0
- echoact/db/schema.sql +214 -0
- echoact/db/store.py +2062 -0
- echoact/diagnostics.py +902 -0
- echoact/domain.py +487 -0
- echoact/engine/__init__.py +0 -0
- echoact/engine/container.py +843 -0
- echoact/engine/protocol.py +241 -0
- echoact/engine/runtime.py +324 -0
- echoact/engine/supervisor.py +961 -0
- echoact/engine/worker.py +659 -0
- echoact/errors.py +281 -0
- echoact/instance.py +172 -0
- echoact/jobs/__init__.py +0 -0
- echoact/jobs/engine.py +776 -0
- echoact/jobs/request.py +300 -0
- echoact/mcp/__init__.py +0 -0
- echoact/mcp/__main__.py +50 -0
- echoact/mcp/client.py +202 -0
- echoact/mcp/config.py +112 -0
- echoact/mcp/server.py +340 -0
- echoact/models/__init__.py +0 -0
- echoact/models/catalog.py +273 -0
- echoact/models/manifest.py +278 -0
- echoact/models/registry.py +1551 -0
- echoact/paths.py +93 -0
- echoact/policy.py +189 -0
- echoact/security/__init__.py +0 -0
- echoact/security/credentials.py +930 -0
- echoact/security/ratelimit.py +534 -0
- echoact/service/__init__.py +20 -0
- echoact/service/app.py +182 -0
- echoact/service/deps.py +563 -0
- echoact/service/errors.py +241 -0
- echoact/service/routes.py +1125 -0
- echoact/service/schemas.py +509 -0
- echoact/service/server.py +270 -0
- echoact/text/__init__.py +0 -0
- echoact/text/language.py +44 -0
- echoact/text/loader.py +577 -0
- echoact/text/normalize.py +924 -0
- echoact/text/segment.py +499 -0
- echoact/text/sniff.py +1202 -0
- echoact/ui/__init__.py +0 -0
- echoact/ui/bridge.py +50 -0
- echoact/ui/controls.py +360 -0
- echoact/ui/credential_dialog.py +131 -0
- echoact/ui/fonts.py +94 -0
- echoact/ui/i18n.py +260 -0
- echoact/ui/icons.py +440 -0
- echoact/ui/library.py +1642 -0
- echoact/ui/licence.py +162 -0
- echoact/ui/main_window.py +1202 -0
- echoact/ui/mcp_setup.py +494 -0
- echoact/ui/models_view.py +1142 -0
- echoact/ui/notifications.py +202 -0
- echoact/ui/reading.py +494 -0
- echoact/ui/settings_view.py +2258 -0
- echoact/ui/status_view.py +1193 -0
- echoact/ui/theme.py +579 -0
- echoact/util/__init__.py +0 -0
- echoact/util/ids.py +62 -0
- echoact/util/logging.py +127 -0
- echoact-0.1.0.dist-info/METADATA +162 -0
- echoact-0.1.0.dist-info/RECORD +80 -0
- echoact-0.1.0.dist-info/WHEEL +4 -0
- echoact-0.1.0.dist-info/entry_points.txt +3 -0
- echoact-0.1.0.dist-info/licenses/LICENSE +21 -0
echoact/ui/licence.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""Presenting a model's use restrictions as terms, per N-11 and F-80.
|
|
2
|
+
|
|
3
|
+
N-11 makes this a release blocker rather than a courtesy: where a model's
|
|
4
|
+
licence obliges a distributor to bind its own users to the same use
|
|
5
|
+
restrictions, the product's terms must carry them through to the end user,
|
|
6
|
+
and shipping such a model without that is a blocker in as many words.
|
|
7
|
+
F-80 fixes when: presented as terms the user accepts *before* that model is
|
|
8
|
+
first prepared, not merely filed among the notices.
|
|
9
|
+
|
|
10
|
+
So this is a dialog with two buttons and no default. It quotes the
|
|
11
|
+
restrictions in the licence's own wording -- a paraphrase is what a reader
|
|
12
|
+
would then hold the distributor to instead of the real term -- and it does
|
|
13
|
+
not let the accept button be the one the Enter key presses, because a term
|
|
14
|
+
accepted by reflex has not been accepted.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from PySide6.QtCore import Qt
|
|
20
|
+
from PySide6.QtWidgets import (
|
|
21
|
+
QDialog,
|
|
22
|
+
QDialogButtonBox,
|
|
23
|
+
QHBoxLayout,
|
|
24
|
+
QLabel,
|
|
25
|
+
QScrollArea,
|
|
26
|
+
QVBoxLayout,
|
|
27
|
+
QWidget,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
from ..models.manifest import ModelEntry
|
|
31
|
+
from . import icons
|
|
32
|
+
from .i18n import add_korean, bytes_size, tr
|
|
33
|
+
from .theme import METRICS, Palette
|
|
34
|
+
|
|
35
|
+
add_korean(
|
|
36
|
+
{
|
|
37
|
+
"Before {model} is prepared": "{model} 준비 전에",
|
|
38
|
+
"This model is published under {licence}. Using it means accepting these "
|
|
39
|
+
"restrictions, which EchoAct is obliged to pass on to you.":
|
|
40
|
+
"이 모델은 {licence} 라이선스로 배포됩니다. 사용하려면 아래 제한 사항에 동의해야 하며, "
|
|
41
|
+
"에코액트는 이를 사용자에게 그대로 전달할 의무가 있습니다.",
|
|
42
|
+
"You may not use the model to:": "다음 목적으로 모델을 사용할 수 없습니다:",
|
|
43
|
+
"Also worth knowing": "함께 알아 두어야 할 사항",
|
|
44
|
+
"Why you are being asked": "이 확인을 요청하는 이유",
|
|
45
|
+
"I accept these restrictions": "제한 사항에 동의합니다",
|
|
46
|
+
"Not now": "지금은 아니오",
|
|
47
|
+
"Download size {size}": "다운로드 크기 {size}",
|
|
48
|
+
"The full licence is in {file} in the model's repository.":
|
|
49
|
+
"전체 라이선스는 모델 저장소의 {file} 파일에 있습니다.",
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class LicenceDialog(QDialog):
|
|
55
|
+
"""Accept or decline one model's use restrictions."""
|
|
56
|
+
|
|
57
|
+
def __init__(self, entry: ModelEntry, palette: Palette, parent: QWidget | None = None) -> None:
|
|
58
|
+
super().__init__(parent)
|
|
59
|
+
self.entry = entry
|
|
60
|
+
terms = entry.license
|
|
61
|
+
m = METRICS
|
|
62
|
+
|
|
63
|
+
self.setWindowTitle(tr("Before {model} is prepared").format(model=entry.display_name))
|
|
64
|
+
self.setModal(True)
|
|
65
|
+
self.setMinimumSize(560, 460)
|
|
66
|
+
|
|
67
|
+
outer = QVBoxLayout(self)
|
|
68
|
+
outer.setContentsMargins(m.pad_wide, m.pad_wide, m.pad_wide, m.pad_wide)
|
|
69
|
+
outer.setSpacing(m.gap_wide)
|
|
70
|
+
|
|
71
|
+
head = QHBoxLayout()
|
|
72
|
+
head.setSpacing(m.gap)
|
|
73
|
+
mark = QLabel()
|
|
74
|
+
mark.setPixmap(icons.pixmap("warning", palette.warn, 22, self.devicePixelRatioF()))
|
|
75
|
+
mark.setAlignment(Qt.AlignmentFlag.AlignTop)
|
|
76
|
+
head.addWidget(mark)
|
|
77
|
+
intro = QLabel(
|
|
78
|
+
tr(
|
|
79
|
+
"This model is published under {licence}. Using it means accepting these "
|
|
80
|
+
"restrictions, which EchoAct is obliged to pass on to you."
|
|
81
|
+
).format(licence=terms.name)
|
|
82
|
+
)
|
|
83
|
+
intro.setWordWrap(True)
|
|
84
|
+
head.addWidget(intro, 1)
|
|
85
|
+
outer.addLayout(head)
|
|
86
|
+
|
|
87
|
+
body = QWidget()
|
|
88
|
+
inner = QVBoxLayout(body)
|
|
89
|
+
inner.setContentsMargins(0, 0, m.gap, 0)
|
|
90
|
+
inner.setSpacing(m.gap)
|
|
91
|
+
|
|
92
|
+
inner.addWidget(self._section(tr("You may not use the model to:")))
|
|
93
|
+
for restriction in terms.restrictions:
|
|
94
|
+
inner.addWidget(self._bullet(restriction))
|
|
95
|
+
|
|
96
|
+
if terms.notes:
|
|
97
|
+
inner.addWidget(self._section(tr("Also worth knowing")))
|
|
98
|
+
for note in terms.notes:
|
|
99
|
+
inner.addWidget(self._bullet(note))
|
|
100
|
+
|
|
101
|
+
inner.addWidget(self._section(tr("Why you are being asked")))
|
|
102
|
+
inner.addWidget(self._bullet(terms.pass_through_obligation))
|
|
103
|
+
inner.addWidget(
|
|
104
|
+
self._muted(
|
|
105
|
+
tr("The full licence is in {file} in the model's repository.").format(
|
|
106
|
+
file=terms.source_file
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
inner.addStretch(1)
|
|
111
|
+
|
|
112
|
+
scroll = QScrollArea()
|
|
113
|
+
scroll.setWidget(body)
|
|
114
|
+
scroll.setWidgetResizable(True)
|
|
115
|
+
scroll.setFrameShape(QScrollArea.Shape.NoFrame)
|
|
116
|
+
outer.addWidget(scroll, 1)
|
|
117
|
+
|
|
118
|
+
outer.addWidget(
|
|
119
|
+
self._muted(
|
|
120
|
+
tr("Download size {size}").format(size=bytes_size(entry.total_bytes))
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
buttons = QDialogButtonBox()
|
|
125
|
+
self.decline = buttons.addButton(
|
|
126
|
+
tr("Not now"), QDialogButtonBox.ButtonRole.RejectRole
|
|
127
|
+
)
|
|
128
|
+
self.accept_button = buttons.addButton(
|
|
129
|
+
tr("I accept these restrictions"), QDialogButtonBox.ButtonRole.AcceptRole
|
|
130
|
+
)
|
|
131
|
+
# Declining is the default. Accepting a licence must be an act, and
|
|
132
|
+
# a dialog whose accept button answers the Enter key turns it into a
|
|
133
|
+
# reflex.
|
|
134
|
+
self.accept_button.setDefault(False)
|
|
135
|
+
self.accept_button.setAutoDefault(False)
|
|
136
|
+
self.decline.setDefault(True)
|
|
137
|
+
self.decline.setProperty("variant", "quiet")
|
|
138
|
+
self.accept_button.setProperty("variant", "primary")
|
|
139
|
+
buttons.accepted.connect(self.accept)
|
|
140
|
+
buttons.rejected.connect(self.reject)
|
|
141
|
+
outer.addWidget(buttons)
|
|
142
|
+
|
|
143
|
+
@staticmethod
|
|
144
|
+
def _section(text: str) -> QLabel:
|
|
145
|
+
lb = QLabel(text)
|
|
146
|
+
lb.setProperty("role", "section")
|
|
147
|
+
lb.setWordWrap(True)
|
|
148
|
+
return lb
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def _bullet(text: str) -> QLabel:
|
|
152
|
+
lb = QLabel("· " + text)
|
|
153
|
+
lb.setWordWrap(True)
|
|
154
|
+
lb.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
|
|
155
|
+
return lb
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def _muted(text: str) -> QLabel:
|
|
159
|
+
lb = QLabel(text)
|
|
160
|
+
lb.setProperty("role", "muted")
|
|
161
|
+
lb.setWordWrap(True)
|
|
162
|
+
return lb
|