power-loop 6.3.0__tar.gz → 6.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {power_loop-6.3.0 → power_loop-6.5.0}/PKG-INFO +1 -1
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/__init__.py +1 -1
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/_vendor/llm_client/multimodal.py +60 -11
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/image_recall.py +38 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop.egg-info/PKG-INFO +1 -1
- {power_loop-6.3.0 → power_loop-6.5.0}/LICENSE +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/README.md +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/_vendor/__init__.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/_vendor/llm_client/__init__.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/_vendor/llm_client/anthropic_factory.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/_vendor/llm_client/capabilities.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/_vendor/llm_client/interface.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/_vendor/llm_client/llm_factory.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/_vendor/llm_client/llm_tooling.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/_vendor/llm_client/llm_utils.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/agent/__init__.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/agent/follow_up.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/agent/sink.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/agent/stateful_loop.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/agent/system_prompt.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/agent/types.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contracts/__init__.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contracts/errors.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contracts/event_payloads.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contracts/events.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contracts/handlers.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contracts/hook_contexts.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contracts/hooks.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contracts/messages.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contracts/protocols.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contracts/tools.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contrib/__init__.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contrib/_redact.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contrib/jsonl_sink.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contrib/logging_sink.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contrib/mcp.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contrib/metrics_sink.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/contrib/otel_sink.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/core/agent_context.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/core/events.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/core/hooks.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/core/phase.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/core/pipeline.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/core/runner.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/core/state.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/py.typed +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/blackboard.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/budget.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/cancellation.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/compact.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/env.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/exec_backend.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/fold.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/fold_adapter.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/history_projector.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/history_sanitize.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/human_input.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/memory.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/notes.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/provider.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/representation.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/retry.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/runtime_state.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/session_store.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/skills.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/spec.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/store/__init__.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/store/backends/__init__.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/store/backends/mysql.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/store/backends/postgres.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/store/backends/sqlite.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/store/capabilities.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/store/db.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/store/dialect.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/store/factory.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/store/schema.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/store/store.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/store/types.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/structured.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/stub_provider.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/runtime/timers.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/tools/__init__.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/tools/blackboard.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/tools/command_policy.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/tools/default_manifest.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/tools/default_tools.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/tools/registry.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/tools/spawn_agent.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/workflow/__init__.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/workflow/api.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/workflow/engine.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/workflow/introspect.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/workflow/journal.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/workflow/result.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/workflow/resume.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/workflow/runner.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/workflow/spec.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/workflow/subprocess_executor.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/workflow/tool.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop/workflow/worker.py +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop.egg-info/SOURCES.txt +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop.egg-info/dependency_links.txt +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop.egg-info/requires.txt +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/power_loop.egg-info/top_level.txt +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/pyproject.toml +0 -0
- {power_loop-6.3.0 → power_loop-6.5.0}/setup.cfg +0 -0
|
@@ -15,7 +15,7 @@ Stability tiers
|
|
|
15
15
|
无版本承诺,可随时变更或删除。
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
__version__ = "6.
|
|
18
|
+
__version__ = "6.5.0"
|
|
19
19
|
|
|
20
20
|
# Public LLM contract (SDK-free) re-exported so callers (e.g. writing llm.* hooks or
|
|
21
21
|
# a custom LLMService) don't reach into the internal vendored transport package (H3.4).
|
|
@@ -39,8 +39,25 @@ class PreparedAttachment:
|
|
|
39
39
|
strategy: str = "text"
|
|
40
40
|
|
|
41
41
|
|
|
42
|
+
#: 扩展名 → MIME 的**内置**兜底表。不能只靠 `mimetypes`:它读的是系统的 mime.types,
|
|
43
|
+
#: 不同镜像装的不一样。真实事故(DeepTalk conv-201):宿主 Python 认得 `.webp`,
|
|
44
|
+
#: 生产容器里 `guess_type(".webp")` 返回 None → 落到 application/octet-stream →
|
|
45
|
+
#: 渲染层判定「这不是图片」抛 ModelCapabilityError → 重试耗尽 → 整个 run 降级终止。
|
|
46
|
+
#: agent 刚把配图处理成 webp、正要看一眼,会话就死在这里。本地全绿、生产炸掉的典型。
|
|
47
|
+
_EXT_MIME_FALLBACK = {
|
|
48
|
+
".webp": "image/webp", ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg",
|
|
49
|
+
".gif": "image/gif", ".bmp": "image/bmp", ".tif": "image/tiff", ".tiff": "image/tiff",
|
|
50
|
+
".avif": "image/avif", ".heic": "image/heic", ".heif": "image/heif", ".svg": "image/svg+xml",
|
|
51
|
+
".pdf": "application/pdf",
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
42
55
|
def _guess_mime_type(path: Path) -> str:
|
|
56
|
+
"""文件的 MIME。系统表优先,认不出来再查内置表 —— 顺序不能反:
|
|
57
|
+
系统表更全,内置表只补它在某些镜像里缺的那几个。"""
|
|
43
58
|
mime_type, _ = mimetypes.guess_type(str(path))
|
|
59
|
+
if not mime_type:
|
|
60
|
+
mime_type = _EXT_MIME_FALLBACK.get(path.suffix.lower())
|
|
44
61
|
return mime_type or "application/octet-stream"
|
|
45
62
|
|
|
46
63
|
|
|
@@ -152,6 +169,10 @@ def _downscale_to_data_url(path: Path, mime_type: str, max_edge: int | None) ->
|
|
|
152
169
|
resized.save(buf, format="JPEG", quality=85, optimize=True)
|
|
153
170
|
payload = base64.b64encode(buf.getvalue()).decode("ascii")
|
|
154
171
|
return f"data:image/jpeg;base64,{payload}"
|
|
172
|
+
except OSError:
|
|
173
|
+
# 文件读不到不是「缩放失败」——报 "sending it at original size" 会把调用方引到
|
|
174
|
+
# 图像解码上,而真正的毛病在路径。交给上层按「读不到」降级。
|
|
175
|
+
raise
|
|
155
176
|
except Exception: # noqa: BLE001 — a decode failure must not lose the image entirely
|
|
156
177
|
logger.warning("could not downscale %s; sending it at original size", path.name,
|
|
157
178
|
exc_info=True)
|
|
@@ -171,12 +192,27 @@ def _render_image_attachment(ref: AttachmentRef, path: Path, capabilities: Model
|
|
|
171
192
|
调用方想要「发图给看不了图的模型就报错」,用 ``capabilities.require_image_input()`` 自查。
|
|
172
193
|
"""
|
|
173
194
|
if capabilities.supports_image_input is True:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
195
|
+
try:
|
|
196
|
+
url = _downscale_to_data_url(path, ref.mime_type, capabilities.max_image_edge)
|
|
197
|
+
except OSError as exc:
|
|
198
|
+
# 文件读不到(路径解析错、被清理、权限)——**降级,不抛**。抛出去的代价与
|
|
199
|
+
# 能力不匹配那一路完全一样:一次 render 同时渲染历史与本轮,一个读不到的
|
|
200
|
+
# 附件会让每一次 send 都失败,重试耗尽后整个 run 终止。DeepTalk conv-198
|
|
201
|
+
# 真实发生过:两张刚生成的图因相对路径被按进程 cwd 解析而找不到,agent 就此
|
|
202
|
+
# 停在半路,图既没被看到也没发出去。占位文本同样必须说清「你没有看到」。
|
|
203
|
+
logger.warning("image %s could not be read (%s); surfaced as a text placeholder",
|
|
204
|
+
ref.filename, exc)
|
|
205
|
+
text = (
|
|
206
|
+
f"[图片 {describe_attachment_ref(ref)}——**这个文件读不到,你没有看到它的内容**。"
|
|
207
|
+
"不要凭空描述这张图;需要看就按上面的坐标用视觉工具重新取它。]"
|
|
208
|
+
)
|
|
209
|
+
return PreparedAttachment(
|
|
210
|
+
ref=ref,
|
|
211
|
+
text_fallback=text,
|
|
212
|
+
rendered_parts=({"type": "text", "text": text},),
|
|
213
|
+
strategy="image-unreadable",
|
|
214
|
+
)
|
|
215
|
+
part = {"type": "image_url", "image_url": {"url": url}}
|
|
180
216
|
return PreparedAttachment(ref=ref, rendered_parts=(part,), strategy="native-image")
|
|
181
217
|
|
|
182
218
|
logger.warning(
|
|
@@ -246,11 +282,24 @@ def prepare_attachment(ref_payload: dict[str, Any], capabilities: ModelCapabilit
|
|
|
246
282
|
if ref.kind == "pdf":
|
|
247
283
|
return _render_pdf_attachment(ref, path, capabilities)
|
|
248
284
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
285
|
+
# 认不出的类型:降级成**明说没读到**的占位,不抛。
|
|
286
|
+
#
|
|
287
|
+
# 原先这里抛 ModelCapabilityError,理由是「占位符读起来像文件已经被读过」——那个理由
|
|
288
|
+
# 对含糊的占位成立,对下面这句不成立:它明确写着模型没有拿到内容,并给出回取坐标。
|
|
289
|
+
# 而抛出去的代价是整个会话不可用:一次 render 同时渲染历史与本轮,历史里躺着一个认不出
|
|
290
|
+
# 类型的附件,之后**每一次** send 都失败。DeepTalk conv-201 就是这么断的——容器里
|
|
291
|
+
# `.webp` 猜不出 MIME(见 _EXT_MIME_FALLBACK),agent 处理完配图正要看一眼,会话就死了。
|
|
292
|
+
logger.warning("attachment %s has unsupported type %r; surfaced as a text placeholder",
|
|
293
|
+
ref.filename, ref.mime_type)
|
|
294
|
+
text = (
|
|
295
|
+
f"[附件 {describe_attachment_ref(ref)}(类型 {ref.mime_type})——**这个类型发不进上下文,"
|
|
296
|
+
"你没有读到它的内容**。不要凭空描述它;需要的话先把它转成图片或可提取文本的 PDF。]"
|
|
297
|
+
)
|
|
298
|
+
return PreparedAttachment(
|
|
299
|
+
ref=ref,
|
|
300
|
+
text_fallback=text,
|
|
301
|
+
rendered_parts=({"type": "text", "text": text},),
|
|
302
|
+
strategy="unsupported-type",
|
|
254
303
|
)
|
|
255
304
|
|
|
256
305
|
|
|
@@ -28,6 +28,7 @@ Pass ``durable=False`` for the look-once case (request-only, never stored).
|
|
|
28
28
|
from __future__ import annotations
|
|
29
29
|
|
|
30
30
|
import threading
|
|
31
|
+
from collections.abc import Sequence
|
|
31
32
|
from typing import Any
|
|
32
33
|
|
|
33
34
|
LoopMessage = dict[str, Any]
|
|
@@ -77,6 +78,43 @@ def queue_image_for_next_round(
|
|
|
77
78
|
return True
|
|
78
79
|
|
|
79
80
|
|
|
81
|
+
def queue_images_for_next_round(
|
|
82
|
+
session_id: str | None,
|
|
83
|
+
images: Sequence[tuple[str, str]],
|
|
84
|
+
*,
|
|
85
|
+
note: str = "",
|
|
86
|
+
durable: bool = True,
|
|
87
|
+
) -> int:
|
|
88
|
+
"""Queue a BATCH as ONE user message: a single note + one attachment block per image.
|
|
89
|
+
|
|
90
|
+
Queuing them one by one produces N separate user turns, each repeating the note — three
|
|
91
|
+
screenshots asked about with one question became three copies of that question in the
|
|
92
|
+
transcript. A batch is one turn, which is also what the provider APIs expect.
|
|
93
|
+
|
|
94
|
+
``images`` is ``[(path, ref)]``. Returns how many were accepted (0 = nothing queued, so the
|
|
95
|
+
caller must say so rather than claim the pictures were delivered).
|
|
96
|
+
"""
|
|
97
|
+
if not session_id:
|
|
98
|
+
return 0
|
|
99
|
+
blocks: list[dict[str, Any]] = []
|
|
100
|
+
if note:
|
|
101
|
+
blocks.append({"type": "text", "text": note})
|
|
102
|
+
accepted = 0
|
|
103
|
+
for path, ref in images:
|
|
104
|
+
if not path:
|
|
105
|
+
continue
|
|
106
|
+
blocks.append({"type": "attachment", "attachment": _attachment_ref(path, ref=ref)})
|
|
107
|
+
accepted += 1
|
|
108
|
+
if not accepted:
|
|
109
|
+
return 0
|
|
110
|
+
with _lock:
|
|
111
|
+
queue = _pending.setdefault(session_id, [])
|
|
112
|
+
if len(queue) >= MAX_PENDING_PER_SESSION:
|
|
113
|
+
return 0
|
|
114
|
+
queue.append({"role": "user", "content": blocks, "__durable__": durable})
|
|
115
|
+
return accepted
|
|
116
|
+
|
|
117
|
+
|
|
80
118
|
def drain_queued_images(session_id: str | None) -> tuple[list[LoopMessage], list[LoopMessage]]:
|
|
81
119
|
"""Take everything queued for this session (and clear it).
|
|
82
120
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|