modus-sdk 0.2.2__tar.gz → 0.2.3__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.
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/CHANGELOG.md +2 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/PKG-INFO +1 -1
- modus_sdk-0.2.3/modus/_agent_run_request.py +22 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_chat.py +2 -3
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_http.py +60 -2
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_version.py +2 -2
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/workflows/runs.py +3 -1
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/.gitignore +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/CONTRIBUTING.md +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/LICENSE +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/README.md +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/assets/modus-logo.png +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/assets/modus-logo.svg +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/examples/apps/modus_skill_chat/.streamlit/config.toml +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/examples/apps/modus_skill_chat/README.md +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/examples/apps/modus_skill_chat/app.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/examples/apps/modus_skill_chat/pyproject.toml +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/examples/scripts/chat.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/examples/scripts/manage_skill.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/examples/scripts/modus_chat.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/examples/scripts/quickstart.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_auth.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_client.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_config.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_deprecation.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_exceptions.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_generated/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_generated/models.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_generated/operations.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_openapi_invoke.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_pagination.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_query.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_retry.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_streaming.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/_validation.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/_client.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/_access_config_write.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/context/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/context/_content_merge.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/context/context.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/context/custom_items.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/context/items.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/organization.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/scopes/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/scopes/_update_body.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/scopes/conversations.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/scopes/evaluations.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/scopes/memories.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/scopes/scopes.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/scopes/supervision.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/usage.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/users.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/workflows/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/workflows/_update_body.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/workflows/interfaces.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/resources/workflows/workflows.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/_guardrails_access.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/_serialize.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/agent_selection.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/agents.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/context.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/conversations.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/evaluations.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/json_blob.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/memories.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/ownership.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/skills.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/toolset.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/triggers.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/usage.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/management/types/workflow.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/py.typed +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/connections/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/connections/connections.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/context/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/context/context.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/context/items.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/modus/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/modus/conversations.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/modus/modus.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/scopes/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/scopes/conversations.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/scopes/scopes.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/suggestions/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/suggestions/suggestions.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/workflows/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/workflows/workflow_actions.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/resources/workflows/workflows.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/__init__.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/agent_runs.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/agents.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/chat.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/compose.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/connections.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/context.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/context_accessors.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/context_compose.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/context_values.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/conversations.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/json_blob.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/pagination.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/runs.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/skills.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/suggestions.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/tool_calls.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/modus/types/views.py +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/project.json +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/pyproject.toml +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/pyrightconfig.json +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/requirements.txt +0 -0
- {modus_sdk-0.2.2 → modus_sdk-0.2.3}/uv.lock +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Shared agent-host run request helpers (chat_stream + runs.create*)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Mapping, Optional
|
|
6
|
+
from uuid import uuid4
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def resolve_agent_run_session_id(session_id: Optional[str] = None) -> str:
|
|
10
|
+
"""Mint or reuse a non-empty sessionId for agent-host runs."""
|
|
11
|
+
if isinstance(session_id, str) and session_id.strip():
|
|
12
|
+
return session_id.strip()
|
|
13
|
+
return str(uuid4())
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def with_agent_run_session_id(body: Mapping[str, Any]) -> dict[str, Any]:
|
|
17
|
+
"""Copy body and ensure sessionId is set (parity with chat_stream)."""
|
|
18
|
+
out: dict[str, Any] = dict(body)
|
|
19
|
+
out["sessionId"] = resolve_agent_run_session_id(
|
|
20
|
+
out.get("sessionId") if isinstance(out.get("sessionId"), str) else None
|
|
21
|
+
)
|
|
22
|
+
return out
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import uuid
|
|
6
5
|
from typing import Any, AsyncIterator, Dict, Iterator, List, Literal, Optional, Union
|
|
7
6
|
from urllib.parse import quote
|
|
8
7
|
|
|
8
|
+
from ._agent_run_request import resolve_agent_run_session_id
|
|
9
9
|
from ._exceptions import ModusError, RunCancelledError, StreamTimeoutError
|
|
10
10
|
from ._generated.models import SkillChatRequestDto, SkillChatResponseDto
|
|
11
11
|
from ._http import AsyncHttpClient, HttpClient
|
|
@@ -37,8 +37,7 @@ def _effective_thread_id(thread_id: Optional[str]) -> Optional[str]:
|
|
|
37
37
|
|
|
38
38
|
def _resolve_session_id(thread_id: Optional[str]) -> str:
|
|
39
39
|
"""Agent-host runs always require sessionId — reuse thread_id or mint a UUID."""
|
|
40
|
-
|
|
41
|
-
return tid if tid is not None else str(uuid.uuid4())
|
|
40
|
+
return resolve_agent_run_session_id(_effective_thread_id(thread_id))
|
|
42
41
|
|
|
43
42
|
|
|
44
43
|
def _chat_path(
|
|
@@ -174,6 +174,34 @@ async def _raise_for_status_async(response: httpx.Response) -> None:
|
|
|
174
174
|
_raise_for_status_with_body(response, body)
|
|
175
175
|
|
|
176
176
|
|
|
177
|
+
def _ensure_sse_content_type(response: httpx.Response, body_preview: str = "") -> None:
|
|
178
|
+
"""Reject non-SSE responses so empty streams never look like success."""
|
|
179
|
+
ct = (response.headers.get("content-type") or "").lower()
|
|
180
|
+
if "text/event-stream" in ct:
|
|
181
|
+
return
|
|
182
|
+
preview = body_preview[:200]
|
|
183
|
+
raise ModusError(
|
|
184
|
+
f"Unexpected stream response: expected Content-Type text/event-stream, "
|
|
185
|
+
f"got {response.headers.get('content-type')!r}. "
|
|
186
|
+
f"Body preview: {preview}",
|
|
187
|
+
status_code=response.status_code,
|
|
188
|
+
body=preview or None,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _sse_body_preview_from_lines(lines: Iterator[str], limit: int = 200) -> str:
|
|
193
|
+
"""Accumulate at most ``limit`` characters from an iterator of lines."""
|
|
194
|
+
preview = ""
|
|
195
|
+
for line in lines:
|
|
196
|
+
remaining = limit - len(preview)
|
|
197
|
+
if remaining <= 0:
|
|
198
|
+
break
|
|
199
|
+
preview += line[:remaining]
|
|
200
|
+
if len(preview) >= limit:
|
|
201
|
+
break
|
|
202
|
+
return preview
|
|
203
|
+
|
|
204
|
+
|
|
177
205
|
# ---------------------------------------------------------------------------
|
|
178
206
|
# Sync client
|
|
179
207
|
# ---------------------------------------------------------------------------
|
|
@@ -307,8 +335,19 @@ class HttpClient:
|
|
|
307
335
|
"""Make a streaming request and yield raw SSE lines."""
|
|
308
336
|
effective_base_url = base_url or self._config.base_url
|
|
309
337
|
try:
|
|
310
|
-
|
|
338
|
+
# Override client default Accept: application/json — agent-service
|
|
339
|
+
# negotiates SSE vs JSON polling on Accept.
|
|
340
|
+
with self._client.stream(
|
|
341
|
+
method,
|
|
342
|
+
_request_url(path, base_url),
|
|
343
|
+
json=json,
|
|
344
|
+
headers={"Accept": "text/event-stream"},
|
|
345
|
+
) as response:
|
|
311
346
|
_raise_for_status(response)
|
|
347
|
+
ct = (response.headers.get("content-type") or "").lower()
|
|
348
|
+
if "text/event-stream" not in ct:
|
|
349
|
+
preview = _sse_body_preview_from_lines(response.iter_lines())
|
|
350
|
+
_ensure_sse_content_type(response, preview)
|
|
312
351
|
for line in response.iter_lines():
|
|
313
352
|
if line.strip():
|
|
314
353
|
yield line
|
|
@@ -453,8 +492,27 @@ class AsyncHttpClient:
|
|
|
453
492
|
"""Streaming request — yields raw SSE lines."""
|
|
454
493
|
effective_base_url = base_url or self._config.base_url
|
|
455
494
|
try:
|
|
456
|
-
async with self._client.stream(
|
|
495
|
+
async with self._client.stream(
|
|
496
|
+
method,
|
|
497
|
+
_request_url(path, base_url),
|
|
498
|
+
json=json,
|
|
499
|
+
headers={"Accept": "text/event-stream"},
|
|
500
|
+
) as response:
|
|
457
501
|
await _raise_for_status_async(response)
|
|
502
|
+
ct = (response.headers.get("content-type") or "").lower()
|
|
503
|
+
if "text/event-stream" not in ct:
|
|
504
|
+
chunks: list[str] = []
|
|
505
|
+
size = 0
|
|
506
|
+
async for line in response.aiter_lines():
|
|
507
|
+
remaining = 200 - size
|
|
508
|
+
if remaining <= 0:
|
|
509
|
+
break
|
|
510
|
+
piece = line[:remaining]
|
|
511
|
+
chunks.append(piece)
|
|
512
|
+
size += len(piece)
|
|
513
|
+
if size >= 200:
|
|
514
|
+
break
|
|
515
|
+
_ensure_sse_content_type(response, "".join(chunks))
|
|
458
516
|
async for line in response.aiter_lines():
|
|
459
517
|
if line.strip():
|
|
460
518
|
yield line
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.3'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 3)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -15,6 +15,7 @@ from typing import (
|
|
|
15
15
|
Union,
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
+
from ..._agent_run_request import with_agent_run_session_id
|
|
18
19
|
from ..._config import ModusConfig
|
|
19
20
|
from ..._generated.models import (
|
|
20
21
|
ActiveRunsBySessionResponseDto,
|
|
@@ -115,7 +116,8 @@ def _runs_list_params(
|
|
|
115
116
|
|
|
116
117
|
|
|
117
118
|
def _request_body(body: Mapping[str, Any]) -> Dict[str, Any]:
|
|
118
|
-
|
|
119
|
+
"""Copy body and mint sessionId when omitted (parity with chat_stream)."""
|
|
120
|
+
return with_agent_run_session_id(body)
|
|
119
121
|
|
|
120
122
|
|
|
121
123
|
def _optional_body(body: Optional[Mapping[str, Any]]) -> Dict[str, Any]:
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|