neobot-contracts 1.0.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.
- neobot_contracts-1.0.0/PKG-INFO +12 -0
- neobot_contracts-1.0.0/README.md +3 -0
- neobot_contracts-1.0.0/pyproject.toml +19 -0
- neobot_contracts-1.0.0/pyproject.toml.orig +15 -0
- neobot_contracts-1.0.0/src/neobot_contracts/__init__.py +9 -0
- neobot_contracts-1.0.0/src/neobot_contracts/errors.py +5 -0
- neobot_contracts-1.0.0/src/neobot_contracts/events.py +13 -0
- neobot_contracts-1.0.0/src/neobot_contracts/models/__init__.py +34 -0
- neobot_contracts-1.0.0/src/neobot_contracts/models/base.py +38 -0
- neobot_contracts-1.0.0/src/neobot_contracts/models/memory.py +102 -0
- neobot_contracts-1.0.0/src/neobot_contracts/models/scheduled_task.py +58 -0
- neobot_contracts-1.0.0/src/neobot_contracts/models/trigger.py +22 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/__init__.py +116 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/archive_memory_access.py +220 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/clock.py +22 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/creator_image_access.py +62 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/emoji_access.py +58 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/event_source.py +27 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/host.py +126 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/image_analysis_access.py +41 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/logging.py +30 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/media_sender.py +50 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/memory.py +90 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/output.py +79 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/plugin.py +183 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/provider.py +30 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/repository.py +45 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/runtime_event.py +46 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/sandbox.py +103 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/scheduled_task_access.py +75 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/screenshot.py +170 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/trigger.py +28 -0
- neobot_contracts-1.0.0/src/neobot_contracts/ports/unit_of_work.py +39 -0
- neobot_contracts-1.0.0/src/neobot_contracts/py.typed +0 -0
- neobot_contracts-1.0.0/src/neobot_contracts/time_context.py +15 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: neobot-contracts
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Cross-module domain models and port interfaces for NeoBot
|
|
5
|
+
Author: wsrsq, tangtian
|
|
6
|
+
Author-email: wsrsq <wsrsq001@163.com>, tangtian <a14b@126.com>
|
|
7
|
+
Requires-Python: >=3.13
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# neobot-contracts
|
|
11
|
+
|
|
12
|
+
Cross-module domain models and port interfaces for NeoBot.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "neobot-contracts"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Cross-module domain models and port interfaces for NeoBot"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
dependencies = []
|
|
8
|
+
|
|
9
|
+
[[project.authors]]
|
|
10
|
+
name = "wsrsq"
|
|
11
|
+
email = "wsrsq001@163.com"
|
|
12
|
+
|
|
13
|
+
[[project.authors]]
|
|
14
|
+
name = "tangtian"
|
|
15
|
+
email = "a14b@126.com"
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["uv_build>=0.9.27,<0.10.0"]
|
|
19
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "neobot-contracts"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Cross-module domain models and port interfaces for NeoBot"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "wsrsq", email = "wsrsq001@163.com" },
|
|
8
|
+
{ name = "tangtian", email = "a14b@126.com" },
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
dependencies = []
|
|
12
|
+
|
|
13
|
+
[build-system]
|
|
14
|
+
requires = ["uv_build>=0.9.27,<0.10.0"]
|
|
15
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""领域模型"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from neobot_contracts.models.base import ConversationRef, IncomingMessage, MemoryRecord
|
|
6
|
+
from neobot_contracts.models.memory import (
|
|
7
|
+
ArchiveMemory,
|
|
8
|
+
CreatorImageRecord,
|
|
9
|
+
ImageAnalysis,
|
|
10
|
+
TopicNode,
|
|
11
|
+
)
|
|
12
|
+
from neobot_contracts.models.scheduled_task import (
|
|
13
|
+
CompletedScheduledTaskRecord,
|
|
14
|
+
ScheduledTaskRecord,
|
|
15
|
+
ScheduledTaskRecurrence,
|
|
16
|
+
ScheduledTaskState,
|
|
17
|
+
)
|
|
18
|
+
from neobot_contracts.models.trigger import KeywordConfig, TriggerResult
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"ConversationRef",
|
|
22
|
+
"IncomingMessage",
|
|
23
|
+
"MemoryRecord",
|
|
24
|
+
"ArchiveMemory",
|
|
25
|
+
"CreatorImageRecord",
|
|
26
|
+
"ImageAnalysis",
|
|
27
|
+
"TopicNode",
|
|
28
|
+
"CompletedScheduledTaskRecord",
|
|
29
|
+
"ScheduledTaskRecord",
|
|
30
|
+
"ScheduledTaskRecurrence",
|
|
31
|
+
"ScheduledTaskState",
|
|
32
|
+
"KeywordConfig",
|
|
33
|
+
"TriggerResult",
|
|
34
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""基础模型"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Any, Literal
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True, slots=True)
|
|
11
|
+
class ConversationRef:
|
|
12
|
+
"""会话引用,标识一个私聊或群聊"""
|
|
13
|
+
|
|
14
|
+
kind: Literal["private", "group"]
|
|
15
|
+
id: str
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True, slots=True)
|
|
19
|
+
class IncomingMessage:
|
|
20
|
+
"""从适配器接收到的消息,与协议无关的统一表示"""
|
|
21
|
+
|
|
22
|
+
event_id: str
|
|
23
|
+
conversation: ConversationRef
|
|
24
|
+
sender_id: str
|
|
25
|
+
sender_name: str
|
|
26
|
+
text: str
|
|
27
|
+
occurred_at: datetime
|
|
28
|
+
raw_payload: dict[str, Any] | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True, slots=True)
|
|
32
|
+
class MemoryRecord:
|
|
33
|
+
"""记忆条目"""
|
|
34
|
+
|
|
35
|
+
conversation: ConversationRef
|
|
36
|
+
speaker_id: str
|
|
37
|
+
content: str
|
|
38
|
+
created_at: datetime
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""记忆模型"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from enum import Enum
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TopicNodeType(str, Enum):
|
|
11
|
+
"""话题节点类型"""
|
|
12
|
+
|
|
13
|
+
RAW = "raw"
|
|
14
|
+
SUMMARY = "summary"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True, slots=True)
|
|
18
|
+
class ArchiveMemory:
|
|
19
|
+
"""档案式记忆"""
|
|
20
|
+
|
|
21
|
+
id: int
|
|
22
|
+
table_name: str # 表名,用于区分不同类别的记忆
|
|
23
|
+
key: str # 键名,在表内唯一标识一个条目
|
|
24
|
+
value: str
|
|
25
|
+
tags: list[str]
|
|
26
|
+
created_at: datetime
|
|
27
|
+
updated_at: datetime
|
|
28
|
+
version: int
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True, slots=True)
|
|
32
|
+
class TopicNode:
|
|
33
|
+
"""话题向量树节点"""
|
|
34
|
+
|
|
35
|
+
id: int
|
|
36
|
+
title: str
|
|
37
|
+
content: str
|
|
38
|
+
embedding: list[float]
|
|
39
|
+
node_type: TopicNodeType
|
|
40
|
+
tags: list[str]
|
|
41
|
+
parent_id: int | None
|
|
42
|
+
children_ids: list[int]
|
|
43
|
+
created_at: datetime
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True, slots=True)
|
|
47
|
+
class ImageAnalysis:
|
|
48
|
+
"""缓存的图片分析元数据与文本结果。"""
|
|
49
|
+
|
|
50
|
+
id: int
|
|
51
|
+
file_hash: str
|
|
52
|
+
source: str | None
|
|
53
|
+
mime_type: str | None
|
|
54
|
+
original_width: int | None
|
|
55
|
+
original_height: int | None
|
|
56
|
+
processed_width: int | None
|
|
57
|
+
processed_height: int | None
|
|
58
|
+
analysis_text: str | None
|
|
59
|
+
created_at: datetime
|
|
60
|
+
updated_at: datetime
|
|
61
|
+
version: int
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass(frozen=True, slots=True)
|
|
65
|
+
class EmojiRecord:
|
|
66
|
+
"""表情包记录 — 解析后的表情包图片元数据与描述"""
|
|
67
|
+
|
|
68
|
+
id: int
|
|
69
|
+
file_hash: str
|
|
70
|
+
file_name: str
|
|
71
|
+
file_path: str
|
|
72
|
+
mime_type: str | None
|
|
73
|
+
original_width: int | None
|
|
74
|
+
original_height: int | None
|
|
75
|
+
analysis_text: str | None
|
|
76
|
+
use_count: int
|
|
77
|
+
created_at: datetime
|
|
78
|
+
updated_at: datetime
|
|
79
|
+
version: int
|
|
80
|
+
image_source: str | None = None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass(frozen=True, slots=True)
|
|
84
|
+
class CreatorImageRecord:
|
|
85
|
+
"""Creator Agent 图片元数据。"""
|
|
86
|
+
|
|
87
|
+
id: int
|
|
88
|
+
image_id: str
|
|
89
|
+
source: str
|
|
90
|
+
file_hash: str
|
|
91
|
+
file_path: str
|
|
92
|
+
prompt: str | None
|
|
93
|
+
description: str | None
|
|
94
|
+
mime_type: str | None
|
|
95
|
+
original_width: int | None
|
|
96
|
+
original_height: int | None
|
|
97
|
+
created_at: datetime
|
|
98
|
+
updated_at: datetime
|
|
99
|
+
version: int
|
|
100
|
+
image_source: str | None = None
|
|
101
|
+
#: 图库固定编号(只在入库时分配一次,不随排序/更新变化);非图库记录为 None
|
|
102
|
+
gallery_no: int | None = None
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""定时任务领域模型。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from neobot_contracts.models.base import ConversationRef
|
|
11
|
+
from neobot_contracts.time_context import now_utc
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ScheduledTaskRecurrence(StrEnum):
|
|
15
|
+
ONCE = "once"
|
|
16
|
+
DAILY = "daily"
|
|
17
|
+
WEEKLY = "weekly"
|
|
18
|
+
MONTHLY = "monthly"
|
|
19
|
+
YEARLY = "yearly"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ScheduledTaskState(StrEnum):
|
|
23
|
+
ACTIVE = "active"
|
|
24
|
+
DISABLED = "disabled"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True, slots=True)
|
|
28
|
+
class ScheduledTaskRecord:
|
|
29
|
+
id: int
|
|
30
|
+
task_uuid: str
|
|
31
|
+
title: str
|
|
32
|
+
detail: str
|
|
33
|
+
recurrence: ScheduledTaskRecurrence
|
|
34
|
+
start_at: datetime
|
|
35
|
+
end_at: datetime
|
|
36
|
+
bindings: tuple[ConversationRef, ...]
|
|
37
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
38
|
+
completed_window_keys: tuple[str, ...] = ()
|
|
39
|
+
state: ScheduledTaskState = ScheduledTaskState.ACTIVE
|
|
40
|
+
created_at: datetime = field(default_factory=now_utc)
|
|
41
|
+
updated_at: datetime = field(default_factory=now_utc)
|
|
42
|
+
version: int = 1
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True, slots=True)
|
|
46
|
+
class CompletedScheduledTaskRecord:
|
|
47
|
+
id: int
|
|
48
|
+
task_uuid: str
|
|
49
|
+
title: str
|
|
50
|
+
detail: str
|
|
51
|
+
recurrence: ScheduledTaskRecurrence
|
|
52
|
+
start_at: datetime
|
|
53
|
+
end_at: datetime
|
|
54
|
+
bindings: tuple[ConversationRef, ...]
|
|
55
|
+
metadata: dict[str, Any]
|
|
56
|
+
completed_at: datetime
|
|
57
|
+
completion_reason: str
|
|
58
|
+
archived_payload: dict[str, Any]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""触发器模型"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True, slots=True)
|
|
9
|
+
class KeywordConfig:
|
|
10
|
+
"""关键词配置"""
|
|
11
|
+
|
|
12
|
+
keyword: str
|
|
13
|
+
weight: float
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True, slots=True)
|
|
17
|
+
class TriggerResult:
|
|
18
|
+
"""触发结果"""
|
|
19
|
+
|
|
20
|
+
should_reply: bool
|
|
21
|
+
probability: float
|
|
22
|
+
matched_keywords: list[str]
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Port 接口集合"""
|
|
2
|
+
|
|
3
|
+
from neobot_contracts.ports.logging import Logger, NullLogger
|
|
4
|
+
from neobot_contracts.ports.output import CapturingOutput, NullOutput, OutputMessage, OutputPort
|
|
5
|
+
from neobot_contracts.ports.runtime_event import RuntimeEnvelope, RuntimeInterceptionRegistry
|
|
6
|
+
from neobot_contracts.ports.clock import Clock, SystemClock, now_utc
|
|
7
|
+
from neobot_contracts.ports.event_source import EventSource, Subscription
|
|
8
|
+
from neobot_contracts.ports.plugin import (
|
|
9
|
+
Plugin,
|
|
10
|
+
PluginCapability,
|
|
11
|
+
PluginHandle,
|
|
12
|
+
PluginLoader,
|
|
13
|
+
PluginManager,
|
|
14
|
+
PluginRegistry,
|
|
15
|
+
PluginState,
|
|
16
|
+
RuntimePluginContext,
|
|
17
|
+
)
|
|
18
|
+
from neobot_contracts.ports.repository import MemoryRepository, MessageRepository, ProfileRepository
|
|
19
|
+
from neobot_contracts.ports.provider import Provider
|
|
20
|
+
from neobot_contracts.ports.unit_of_work import UnitOfWork, UnitOfWorkFactory
|
|
21
|
+
from neobot_contracts.ports.creator_image_access import CreatorImageAccess
|
|
22
|
+
from neobot_contracts.ports.archive_memory_access import (
|
|
23
|
+
ArchiveMemoryAccess,
|
|
24
|
+
ArchiveMemoryAccessWrapper,
|
|
25
|
+
ensure_optional_str,
|
|
26
|
+
ensure_str,
|
|
27
|
+
)
|
|
28
|
+
from neobot_contracts.ports.image_analysis_access import ImageAnalysisAccess
|
|
29
|
+
from neobot_contracts.ports.host import (
|
|
30
|
+
CapabilityRegistry,
|
|
31
|
+
CapabilitySpec,
|
|
32
|
+
CommandRegistry,
|
|
33
|
+
CommandSpec,
|
|
34
|
+
HostRuntimeFacade,
|
|
35
|
+
LifecycleHooks,
|
|
36
|
+
QueryRegistry,
|
|
37
|
+
QuerySpec,
|
|
38
|
+
)
|
|
39
|
+
from neobot_contracts.ports.scheduled_task_access import ScheduledTaskAccess
|
|
40
|
+
from neobot_contracts.ports.sandbox import SandboxDataPort
|
|
41
|
+
from neobot_contracts.ports.screenshot import (
|
|
42
|
+
FontFace,
|
|
43
|
+
FontFormat,
|
|
44
|
+
FontLoadError,
|
|
45
|
+
ImageFormat,
|
|
46
|
+
InvalidScreenshotOptions,
|
|
47
|
+
RenderOptions,
|
|
48
|
+
ScreenshotError,
|
|
49
|
+
ScreenshotMode,
|
|
50
|
+
ScreenshotOptions,
|
|
51
|
+
ScreenshotPort,
|
|
52
|
+
ScreenshotResult,
|
|
53
|
+
ScreenshotTargetNotFound,
|
|
54
|
+
ScreenshotTimeout,
|
|
55
|
+
ScreenshotUnavailable,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
"Logger",
|
|
60
|
+
"NullLogger",
|
|
61
|
+
"OutputMessage",
|
|
62
|
+
"OutputPort",
|
|
63
|
+
"NullOutput",
|
|
64
|
+
"CapturingOutput",
|
|
65
|
+
"RuntimeEnvelope",
|
|
66
|
+
"RuntimeInterceptionRegistry",
|
|
67
|
+
"Clock",
|
|
68
|
+
"SystemClock",
|
|
69
|
+
"now_utc",
|
|
70
|
+
"EventSource",
|
|
71
|
+
"Subscription",
|
|
72
|
+
"Plugin",
|
|
73
|
+
"PluginCapability",
|
|
74
|
+
"PluginHandle",
|
|
75
|
+
"PluginLoader",
|
|
76
|
+
"PluginManager",
|
|
77
|
+
"PluginRegistry",
|
|
78
|
+
"PluginState",
|
|
79
|
+
"RuntimePluginContext",
|
|
80
|
+
"MemoryRepository",
|
|
81
|
+
"MessageRepository",
|
|
82
|
+
"ProfileRepository",
|
|
83
|
+
"Provider",
|
|
84
|
+
"UnitOfWork",
|
|
85
|
+
"UnitOfWorkFactory",
|
|
86
|
+
"CreatorImageAccess",
|
|
87
|
+
"ArchiveMemoryAccess",
|
|
88
|
+
"ArchiveMemoryAccessWrapper",
|
|
89
|
+
"ensure_optional_str",
|
|
90
|
+
"ensure_str",
|
|
91
|
+
"ImageAnalysisAccess",
|
|
92
|
+
"ScheduledTaskAccess",
|
|
93
|
+
"CapabilityRegistry",
|
|
94
|
+
"CapabilitySpec",
|
|
95
|
+
"CommandRegistry",
|
|
96
|
+
"CommandSpec",
|
|
97
|
+
"HostRuntimeFacade",
|
|
98
|
+
"LifecycleHooks",
|
|
99
|
+
"QueryRegistry",
|
|
100
|
+
"QuerySpec",
|
|
101
|
+
"SandboxDataPort",
|
|
102
|
+
"FontFace",
|
|
103
|
+
"FontFormat",
|
|
104
|
+
"FontLoadError",
|
|
105
|
+
"ImageFormat",
|
|
106
|
+
"InvalidScreenshotOptions",
|
|
107
|
+
"RenderOptions",
|
|
108
|
+
"ScreenshotError",
|
|
109
|
+
"ScreenshotMode",
|
|
110
|
+
"ScreenshotOptions",
|
|
111
|
+
"ScreenshotPort",
|
|
112
|
+
"ScreenshotResult",
|
|
113
|
+
"ScreenshotTargetNotFound",
|
|
114
|
+
"ScreenshotTimeout",
|
|
115
|
+
"ScreenshotUnavailable",
|
|
116
|
+
]
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""Archive Memory Access Port — 档案式记忆读写接口
|
|
2
|
+
|
|
3
|
+
提供通过表名和键名获取和更新档案式记忆条目的函数接口。
|
|
4
|
+
如果输入的表名或键名是 int、float、bool 等类型,会自动转换成 str。
|
|
5
|
+
|
|
6
|
+
示例:
|
|
7
|
+
```python
|
|
8
|
+
from neobot_contracts.ports import ArchiveMemoryAccess
|
|
9
|
+
|
|
10
|
+
class MyArchiveMemoryAccess(ArchiveMemoryAccess):
|
|
11
|
+
async def get(self, table_name: str, key: str):
|
|
12
|
+
# 实现获取逻辑
|
|
13
|
+
...
|
|
14
|
+
# 其他方法...
|
|
15
|
+
|
|
16
|
+
# 使用包装器自动类型转换
|
|
17
|
+
from neobot_contracts.ports import ArchiveMemoryAccessWrapper, ensure_str
|
|
18
|
+
|
|
19
|
+
access = MyArchiveMemoryAccess()
|
|
20
|
+
wrapper = ArchiveMemoryAccessWrapper(access)
|
|
21
|
+
|
|
22
|
+
# 自动将整数键转换为字符串
|
|
23
|
+
memory = await wrapper.get("users", 123)
|
|
24
|
+
```
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from typing import TYPE_CHECKING, Optional, Protocol, runtime_checkable, Union
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from neobot_contracts.models.memory import ArchiveMemory
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@runtime_checkable
|
|
36
|
+
class ArchiveMemoryAccess(Protocol):
|
|
37
|
+
"""档案式记忆读写接口
|
|
38
|
+
|
|
39
|
+
提供通过表名和键名获取和更新档案式记忆条目的功能。
|
|
40
|
+
如果输入的表名或键名是 int 等类型,会自动转换成 str。
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
async def get(
|
|
44
|
+
self,
|
|
45
|
+
table_name: str,
|
|
46
|
+
key: str
|
|
47
|
+
) -> Optional[ArchiveMemory]:
|
|
48
|
+
"""根据表名和键名获取档案式记忆条目
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
table_name: 表名,用于区分不同类别的记忆
|
|
52
|
+
key: 键名,在表内唯一标识一个条目
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
如果找到则返回 ArchiveMemory 对象,否则返回 None
|
|
56
|
+
"""
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
async def set(
|
|
60
|
+
self,
|
|
61
|
+
table_name: str,
|
|
62
|
+
key: str,
|
|
63
|
+
value: str,
|
|
64
|
+
tags: list[str],
|
|
65
|
+
) -> ArchiveMemory:
|
|
66
|
+
"""创建或更新档案式记忆条目
|
|
67
|
+
|
|
68
|
+
如果指定的表名和键名已存在,则更新该条目;
|
|
69
|
+
如果不存在,则创建新条目。
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
table_name: 表名
|
|
73
|
+
key: 键名
|
|
74
|
+
value: 值
|
|
75
|
+
tags: 标签列表
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
创建或更新后的 ArchiveMemory 对象
|
|
79
|
+
"""
|
|
80
|
+
...
|
|
81
|
+
|
|
82
|
+
async def delete(
|
|
83
|
+
self,
|
|
84
|
+
table_name: str,
|
|
85
|
+
key: str,
|
|
86
|
+
) -> bool:
|
|
87
|
+
"""删除档案式记忆条目
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
table_name: 表名
|
|
91
|
+
key: 键名
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
如果成功删除返回 True,如果条目不存在返回 False
|
|
95
|
+
"""
|
|
96
|
+
...
|
|
97
|
+
|
|
98
|
+
async def exists(
|
|
99
|
+
self,
|
|
100
|
+
table_name: str,
|
|
101
|
+
key: str,
|
|
102
|
+
) -> bool:
|
|
103
|
+
"""检查档案式记忆条目是否存在
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
table_name: 表名
|
|
107
|
+
key: 键名
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
如果条目存在返回 True,否则返回 False
|
|
111
|
+
"""
|
|
112
|
+
...
|
|
113
|
+
|
|
114
|
+
async def list(
|
|
115
|
+
self,
|
|
116
|
+
table_name: str,
|
|
117
|
+
*,
|
|
118
|
+
tags: Optional[list[str]] = None,
|
|
119
|
+
key_query: Optional[str] = None,
|
|
120
|
+
value_query: Optional[str] = None,
|
|
121
|
+
limit: int = 50,
|
|
122
|
+
offset: int = 0,
|
|
123
|
+
) -> list[ArchiveMemory]:
|
|
124
|
+
"""按条件列出给定表的档案式记忆条目"""
|
|
125
|
+
...
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def ensure_str(value: Union[str, int, float, bool, None]) -> str:
|
|
129
|
+
"""确保输入值为字符串类型
|
|
130
|
+
|
|
131
|
+
如果输入值是 int、float、bool 等类型,会自动转换为 str。
|
|
132
|
+
如果输入值是 None,会抛出 ValueError。
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
value: 输入值
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
字符串表示
|
|
139
|
+
"""
|
|
140
|
+
if value is None:
|
|
141
|
+
raise ValueError("table_name and key cannot be None")
|
|
142
|
+
return str(value)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def ensure_optional_str(value: Union[str, int, float, bool, None]) -> Optional[str]:
|
|
146
|
+
"""确保输入值为可选字符串类型"""
|
|
147
|
+
if value is None:
|
|
148
|
+
return None
|
|
149
|
+
return str(value)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class ArchiveMemoryAccessWrapper:
|
|
153
|
+
"""ArchiveMemoryAccess 包装器,自动处理类型转换"""
|
|
154
|
+
|
|
155
|
+
def __init__(self, access: ArchiveMemoryAccess):
|
|
156
|
+
self._access = access
|
|
157
|
+
|
|
158
|
+
async def get(
|
|
159
|
+
self,
|
|
160
|
+
table_name: Union[str, int, float, bool, None],
|
|
161
|
+
key: Union[str, int, float, bool, None],
|
|
162
|
+
) -> Optional[ArchiveMemory]:
|
|
163
|
+
"""根据表名和键名获取档案式记忆条目(自动类型转换)"""
|
|
164
|
+
table_name_str = ensure_str(table_name)
|
|
165
|
+
key_str = ensure_str(key)
|
|
166
|
+
return await self._access.get(table_name_str, key_str)
|
|
167
|
+
|
|
168
|
+
async def set(
|
|
169
|
+
self,
|
|
170
|
+
table_name: Union[str, int, float, bool, None],
|
|
171
|
+
key: Union[str, int, float, bool, None],
|
|
172
|
+
value: str,
|
|
173
|
+
tags: list[str],
|
|
174
|
+
) -> ArchiveMemory:
|
|
175
|
+
"""创建或更新档案式记忆条目(自动类型转换)"""
|
|
176
|
+
table_name_str = ensure_str(table_name)
|
|
177
|
+
key_str = ensure_str(key)
|
|
178
|
+
return await self._access.set(table_name_str, key_str, value, tags)
|
|
179
|
+
|
|
180
|
+
async def delete(
|
|
181
|
+
self,
|
|
182
|
+
table_name: Union[str, int, float, bool, None],
|
|
183
|
+
key: Union[str, int, float, bool, None],
|
|
184
|
+
) -> bool:
|
|
185
|
+
"""删除档案式记忆条目(自动类型转换)"""
|
|
186
|
+
table_name_str = ensure_str(table_name)
|
|
187
|
+
key_str = ensure_str(key)
|
|
188
|
+
return await self._access.delete(table_name_str, key_str)
|
|
189
|
+
|
|
190
|
+
async def exists(
|
|
191
|
+
self,
|
|
192
|
+
table_name: Union[str, int, float, bool, None],
|
|
193
|
+
key: Union[str, int, float, bool, None],
|
|
194
|
+
) -> bool:
|
|
195
|
+
"""检查档案式记忆条目是否存在(自动类型转换)"""
|
|
196
|
+
table_name_str = ensure_str(table_name)
|
|
197
|
+
key_str = ensure_str(key)
|
|
198
|
+
return await self._access.exists(table_name_str, key_str)
|
|
199
|
+
|
|
200
|
+
async def list(
|
|
201
|
+
self,
|
|
202
|
+
table_name: Union[str, int, float, bool, None],
|
|
203
|
+
*,
|
|
204
|
+
tags: Optional[list[Union[str, int, float, bool, None]]] = None,
|
|
205
|
+
key_query: Union[str, int, float, bool, None] = None,
|
|
206
|
+
value_query: Union[str, int, float, bool, None] = None,
|
|
207
|
+
limit: int = 50,
|
|
208
|
+
offset: int = 0,
|
|
209
|
+
) -> list[ArchiveMemory]:
|
|
210
|
+
"""列出档案式记忆条目(自动类型转换)"""
|
|
211
|
+
table_name_str = ensure_str(table_name)
|
|
212
|
+
normalized_tags = [ensure_str(tag) for tag in tags] if tags is not None else None
|
|
213
|
+
return await self._access.list(
|
|
214
|
+
table_name_str,
|
|
215
|
+
tags=normalized_tags,
|
|
216
|
+
key_query=ensure_optional_str(key_query),
|
|
217
|
+
value_query=ensure_optional_str(value_query),
|
|
218
|
+
limit=limit,
|
|
219
|
+
offset=offset,
|
|
220
|
+
)
|