@elizaos/python 2.0.0-alpha.10
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.
- package/LICENSE +26 -0
- package/README.md +239 -0
- package/elizaos/__init__.py +280 -0
- package/elizaos/action_docs.py +149 -0
- package/elizaos/advanced_capabilities/__init__.py +85 -0
- package/elizaos/advanced_capabilities/actions/__init__.py +54 -0
- package/elizaos/advanced_capabilities/actions/add_contact.py +139 -0
- package/elizaos/advanced_capabilities/actions/follow_room.py +151 -0
- package/elizaos/advanced_capabilities/actions/image_generation.py +148 -0
- package/elizaos/advanced_capabilities/actions/mute_room.py +164 -0
- package/elizaos/advanced_capabilities/actions/remove_contact.py +145 -0
- package/elizaos/advanced_capabilities/actions/roles.py +207 -0
- package/elizaos/advanced_capabilities/actions/schedule_follow_up.py +154 -0
- package/elizaos/advanced_capabilities/actions/search_contacts.py +145 -0
- package/elizaos/advanced_capabilities/actions/send_message.py +187 -0
- package/elizaos/advanced_capabilities/actions/settings.py +151 -0
- package/elizaos/advanced_capabilities/actions/unfollow_room.py +164 -0
- package/elizaos/advanced_capabilities/actions/unmute_room.py +164 -0
- package/elizaos/advanced_capabilities/actions/update_contact.py +164 -0
- package/elizaos/advanced_capabilities/actions/update_entity.py +161 -0
- package/elizaos/advanced_capabilities/evaluators/__init__.py +18 -0
- package/elizaos/advanced_capabilities/evaluators/reflection.py +134 -0
- package/elizaos/advanced_capabilities/evaluators/relationship_extraction.py +203 -0
- package/elizaos/advanced_capabilities/providers/__init__.py +36 -0
- package/elizaos/advanced_capabilities/providers/agent_settings.py +60 -0
- package/elizaos/advanced_capabilities/providers/contacts.py +77 -0
- package/elizaos/advanced_capabilities/providers/facts.py +82 -0
- package/elizaos/advanced_capabilities/providers/follow_ups.py +113 -0
- package/elizaos/advanced_capabilities/providers/knowledge.py +83 -0
- package/elizaos/advanced_capabilities/providers/relationships.py +112 -0
- package/elizaos/advanced_capabilities/providers/roles.py +97 -0
- package/elizaos/advanced_capabilities/providers/settings.py +51 -0
- package/elizaos/advanced_capabilities/services/__init__.py +18 -0
- package/elizaos/advanced_capabilities/services/follow_up.py +138 -0
- package/elizaos/advanced_capabilities/services/rolodex.py +244 -0
- package/elizaos/advanced_memory/__init__.py +3 -0
- package/elizaos/advanced_memory/evaluators.py +97 -0
- package/elizaos/advanced_memory/memory_service.py +556 -0
- package/elizaos/advanced_memory/plugin.py +30 -0
- package/elizaos/advanced_memory/prompts.py +12 -0
- package/elizaos/advanced_memory/providers.py +90 -0
- package/elizaos/advanced_memory/types.py +65 -0
- package/elizaos/advanced_planning/__init__.py +10 -0
- package/elizaos/advanced_planning/actions.py +145 -0
- package/elizaos/advanced_planning/message_classifier.py +127 -0
- package/elizaos/advanced_planning/planning_service.py +712 -0
- package/elizaos/advanced_planning/plugin.py +40 -0
- package/elizaos/advanced_planning/prompts.py +4 -0
- package/elizaos/basic_capabilities/__init__.py +66 -0
- package/elizaos/basic_capabilities/actions/__init__.py +24 -0
- package/elizaos/basic_capabilities/actions/choice.py +140 -0
- package/elizaos/basic_capabilities/actions/ignore.py +66 -0
- package/elizaos/basic_capabilities/actions/none.py +56 -0
- package/elizaos/basic_capabilities/actions/reply.py +120 -0
- package/elizaos/basic_capabilities/providers/__init__.py +54 -0
- package/elizaos/basic_capabilities/providers/action_state.py +113 -0
- package/elizaos/basic_capabilities/providers/actions.py +263 -0
- package/elizaos/basic_capabilities/providers/attachments.py +76 -0
- package/elizaos/basic_capabilities/providers/capabilities.py +62 -0
- package/elizaos/basic_capabilities/providers/character.py +113 -0
- package/elizaos/basic_capabilities/providers/choice.py +73 -0
- package/elizaos/basic_capabilities/providers/context_bench.py +44 -0
- package/elizaos/basic_capabilities/providers/current_time.py +58 -0
- package/elizaos/basic_capabilities/providers/entities.py +99 -0
- package/elizaos/basic_capabilities/providers/evaluators.py +54 -0
- package/elizaos/basic_capabilities/providers/providers_list.py +55 -0
- package/elizaos/basic_capabilities/providers/recent_messages.py +85 -0
- package/elizaos/basic_capabilities/providers/time.py +45 -0
- package/elizaos/basic_capabilities/providers/world.py +93 -0
- package/elizaos/basic_capabilities/services/__init__.py +18 -0
- package/elizaos/basic_capabilities/services/embedding.py +122 -0
- package/elizaos/basic_capabilities/services/task.py +178 -0
- package/elizaos/bootstrap/__init__.py +12 -0
- package/elizaos/bootstrap/actions/__init__.py +68 -0
- package/elizaos/bootstrap/actions/add_contact.py +149 -0
- package/elizaos/bootstrap/actions/choice.py +147 -0
- package/elizaos/bootstrap/actions/follow_room.py +151 -0
- package/elizaos/bootstrap/actions/ignore.py +80 -0
- package/elizaos/bootstrap/actions/image_generation.py +135 -0
- package/elizaos/bootstrap/actions/mute_room.py +151 -0
- package/elizaos/bootstrap/actions/none.py +71 -0
- package/elizaos/bootstrap/actions/remove_contact.py +159 -0
- package/elizaos/bootstrap/actions/reply.py +140 -0
- package/elizaos/bootstrap/actions/roles.py +193 -0
- package/elizaos/bootstrap/actions/schedule_follow_up.py +164 -0
- package/elizaos/bootstrap/actions/search_contacts.py +159 -0
- package/elizaos/bootstrap/actions/send_message.py +173 -0
- package/elizaos/bootstrap/actions/settings.py +165 -0
- package/elizaos/bootstrap/actions/unfollow_room.py +151 -0
- package/elizaos/bootstrap/actions/unmute_room.py +151 -0
- package/elizaos/bootstrap/actions/update_contact.py +178 -0
- package/elizaos/bootstrap/actions/update_entity.py +175 -0
- package/elizaos/bootstrap/autonomy/__init__.py +18 -0
- package/elizaos/bootstrap/autonomy/action.py +197 -0
- package/elizaos/bootstrap/autonomy/providers.py +165 -0
- package/elizaos/bootstrap/autonomy/routes.py +171 -0
- package/elizaos/bootstrap/autonomy/service.py +562 -0
- package/elizaos/bootstrap/autonomy/types.py +18 -0
- package/elizaos/bootstrap/evaluators/__init__.py +19 -0
- package/elizaos/bootstrap/evaluators/reflection.py +118 -0
- package/elizaos/bootstrap/evaluators/relationship_extraction.py +192 -0
- package/elizaos/bootstrap/plugin.py +140 -0
- package/elizaos/bootstrap/providers/__init__.py +80 -0
- package/elizaos/bootstrap/providers/action_state.py +71 -0
- package/elizaos/bootstrap/providers/actions.py +256 -0
- package/elizaos/bootstrap/providers/agent_settings.py +63 -0
- package/elizaos/bootstrap/providers/attachments.py +76 -0
- package/elizaos/bootstrap/providers/capabilities.py +66 -0
- package/elizaos/bootstrap/providers/character.py +128 -0
- package/elizaos/bootstrap/providers/choice.py +77 -0
- package/elizaos/bootstrap/providers/contacts.py +78 -0
- package/elizaos/bootstrap/providers/context_bench.py +49 -0
- package/elizaos/bootstrap/providers/current_time.py +56 -0
- package/elizaos/bootstrap/providers/entities.py +99 -0
- package/elizaos/bootstrap/providers/evaluators.py +58 -0
- package/elizaos/bootstrap/providers/facts.py +86 -0
- package/elizaos/bootstrap/providers/follow_ups.py +116 -0
- package/elizaos/bootstrap/providers/knowledge.py +73 -0
- package/elizaos/bootstrap/providers/providers_list.py +59 -0
- package/elizaos/bootstrap/providers/recent_messages.py +85 -0
- package/elizaos/bootstrap/providers/relationships.py +106 -0
- package/elizaos/bootstrap/providers/roles.py +95 -0
- package/elizaos/bootstrap/providers/settings.py +55 -0
- package/elizaos/bootstrap/providers/time.py +45 -0
- package/elizaos/bootstrap/providers/world.py +97 -0
- package/elizaos/bootstrap/services/__init__.py +26 -0
- package/elizaos/bootstrap/services/embedding.py +122 -0
- package/elizaos/bootstrap/services/follow_up.py +138 -0
- package/elizaos/bootstrap/services/rolodex.py +244 -0
- package/elizaos/bootstrap/services/task.py +585 -0
- package/elizaos/bootstrap/types.py +54 -0
- package/elizaos/bootstrap/utils/__init__.py +7 -0
- package/elizaos/bootstrap/utils/xml.py +69 -0
- package/elizaos/character.py +149 -0
- package/elizaos/logger.py +179 -0
- package/elizaos/media/__init__.py +45 -0
- package/elizaos/media/mime.py +315 -0
- package/elizaos/media/search.py +161 -0
- package/elizaos/media/tests/__init__.py +1 -0
- package/elizaos/media/tests/test_mime.py +117 -0
- package/elizaos/media/tests/test_search.py +156 -0
- package/elizaos/plugin.py +191 -0
- package/elizaos/prompts.py +1071 -0
- package/elizaos/py.typed +0 -0
- package/elizaos/runtime.py +2572 -0
- package/elizaos/services/__init__.py +49 -0
- package/elizaos/services/hook_service.py +511 -0
- package/elizaos/services/message_service.py +1248 -0
- package/elizaos/settings.py +182 -0
- package/elizaos/streaming_context.py +159 -0
- package/elizaos/trajectory_context.py +18 -0
- package/elizaos/types/__init__.py +512 -0
- package/elizaos/types/agent.py +31 -0
- package/elizaos/types/components.py +208 -0
- package/elizaos/types/database.py +64 -0
- package/elizaos/types/environment.py +46 -0
- package/elizaos/types/events.py +47 -0
- package/elizaos/types/memory.py +45 -0
- package/elizaos/types/model.py +393 -0
- package/elizaos/types/plugin.py +188 -0
- package/elizaos/types/primitives.py +100 -0
- package/elizaos/types/runtime.py +460 -0
- package/elizaos/types/service.py +113 -0
- package/elizaos/types/service_interfaces.py +244 -0
- package/elizaos/types/state.py +188 -0
- package/elizaos/types/task.py +29 -0
- package/elizaos/utils/__init__.py +108 -0
- package/elizaos/utils/spec_examples.py +48 -0
- package/elizaos/utils/streaming.py +426 -0
- package/elizaos_atropos_shared/__init__.py +1 -0
- package/elizaos_atropos_shared/canonical_eliza.py +282 -0
- package/package.json +19 -0
- package/pyproject.toml +143 -0
- package/requirements-dev.in +11 -0
- package/requirements-dev.lock +134 -0
- package/requirements.in +9 -0
- package/requirements.lock +64 -0
- package/tests/__init__.py +0 -0
- package/tests/test_action_parameters.py +154 -0
- package/tests/test_actions_provider_examples.py +39 -0
- package/tests/test_advanced_memory_behavior.py +96 -0
- package/tests/test_advanced_memory_flag.py +30 -0
- package/tests/test_advanced_planning_behavior.py +225 -0
- package/tests/test_advanced_planning_flag.py +26 -0
- package/tests/test_autonomy.py +445 -0
- package/tests/test_bootstrap_initialize.py +37 -0
- package/tests/test_character.py +163 -0
- package/tests/test_character_provider.py +231 -0
- package/tests/test_dynamic_prompt_exec.py +561 -0
- package/tests/test_logger_redaction.py +43 -0
- package/tests/test_plugin.py +117 -0
- package/tests/test_runtime.py +422 -0
- package/tests/test_salt_production_enforcement.py +22 -0
- package/tests/test_settings_crypto.py +118 -0
- package/tests/test_streaming.py +295 -0
- package/tests/test_types.py +221 -0
- package/tests/test_uuid_parity.py +46 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""XML parsing utilities for elizaOS."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def parse_key_value_xml(xml: str) -> dict[str, Any] | None:
|
|
10
|
+
"""Parse key-value pairs from XML tags."""
|
|
11
|
+
if not xml or not isinstance(xml, str):
|
|
12
|
+
return None
|
|
13
|
+
|
|
14
|
+
result: dict[str, Any] = {}
|
|
15
|
+
|
|
16
|
+
response_match = re.search(r"<response>(.*?)</response>", xml, re.DOTALL)
|
|
17
|
+
content = response_match.group(1) if response_match else xml
|
|
18
|
+
|
|
19
|
+
tag_pattern = re.compile(r"<(\w+)(?:\s[^>]*)?>([^<]*(?:<(?!/\1>)[^<]*)*)</\1>", re.DOTALL)
|
|
20
|
+
|
|
21
|
+
for match in tag_pattern.finditer(content):
|
|
22
|
+
tag_name = match.group(1)
|
|
23
|
+
tag_value = match.group(2).strip()
|
|
24
|
+
|
|
25
|
+
if "<" in tag_value and ">" in tag_value:
|
|
26
|
+
nested_result = parse_nested_tags(tag_value)
|
|
27
|
+
if nested_result:
|
|
28
|
+
result[tag_name] = nested_result
|
|
29
|
+
else:
|
|
30
|
+
result[tag_name] = tag_value
|
|
31
|
+
else:
|
|
32
|
+
result[tag_name] = tag_value
|
|
33
|
+
|
|
34
|
+
return result if result else None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def parse_nested_tags(content: str) -> dict[str, Any] | list[Any] | None:
|
|
38
|
+
"""Parse nested XML tags."""
|
|
39
|
+
result: dict[str, list[Any]] = {}
|
|
40
|
+
|
|
41
|
+
tag_pattern = re.compile(r"<(\w+)(?:\s[^>]*)?>([^<]*(?:<(?!/\1>)[^<]*)*)</\1>", re.DOTALL)
|
|
42
|
+
|
|
43
|
+
for match in tag_pattern.finditer(content):
|
|
44
|
+
tag_name = match.group(1)
|
|
45
|
+
tag_value = match.group(2).strip()
|
|
46
|
+
|
|
47
|
+
if tag_name not in result:
|
|
48
|
+
result[tag_name] = []
|
|
49
|
+
|
|
50
|
+
if "<" in tag_value and ">" in tag_value:
|
|
51
|
+
nested = parse_nested_tags(tag_value)
|
|
52
|
+
if nested:
|
|
53
|
+
result[tag_name].append(nested)
|
|
54
|
+
else:
|
|
55
|
+
result[tag_name].append(tag_value)
|
|
56
|
+
else:
|
|
57
|
+
result[tag_name].append(tag_value)
|
|
58
|
+
|
|
59
|
+
if not result:
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
simplified: dict[str, Any] = {}
|
|
63
|
+
for key, values in result.items():
|
|
64
|
+
if len(values) == 1:
|
|
65
|
+
simplified[key] = values[0]
|
|
66
|
+
else:
|
|
67
|
+
simplified[key] = values
|
|
68
|
+
|
|
69
|
+
return simplified
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from google.protobuf.json_format import MessageToDict, ParseDict
|
|
9
|
+
|
|
10
|
+
from elizaos.types.agent import Character
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CharacterValidationError(Exception):
|
|
14
|
+
def __init__(self, message: str, errors: list[str] | None = None) -> None:
|
|
15
|
+
super().__init__(message)
|
|
16
|
+
self.errors = errors or []
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CharacterLoadError(Exception):
|
|
20
|
+
def __init__(self, message: str, cause: Exception | None = None) -> None:
|
|
21
|
+
super().__init__(message)
|
|
22
|
+
self.cause = cause
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def parse_character(input_data: str | dict[str, Any] | Character) -> Character:
|
|
26
|
+
if isinstance(input_data, Character):
|
|
27
|
+
return input_data
|
|
28
|
+
|
|
29
|
+
if isinstance(input_data, str):
|
|
30
|
+
# Treat as file path
|
|
31
|
+
return load_character_from_file(input_data)
|
|
32
|
+
|
|
33
|
+
if isinstance(input_data, dict):
|
|
34
|
+
return validate_and_create_character(input_data)
|
|
35
|
+
|
|
36
|
+
raise CharacterValidationError("Invalid character input format")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def load_character_from_file(path: str) -> Character:
|
|
40
|
+
try:
|
|
41
|
+
file_path = Path(path)
|
|
42
|
+
if not file_path.exists():
|
|
43
|
+
raise CharacterLoadError(f"Character file not found: {path}")
|
|
44
|
+
|
|
45
|
+
with open(file_path, encoding="utf-8") as f:
|
|
46
|
+
data = json.load(f)
|
|
47
|
+
|
|
48
|
+
return validate_and_create_character(data)
|
|
49
|
+
except json.JSONDecodeError as e:
|
|
50
|
+
raise CharacterLoadError(f"Invalid JSON in character file: {path}", cause=e) from e
|
|
51
|
+
except CharacterValidationError:
|
|
52
|
+
raise
|
|
53
|
+
except Exception as e:
|
|
54
|
+
raise CharacterLoadError(f"Failed to load character from {path}: {e}", cause=e) from e
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def validate_and_create_character(data: dict[str, Any]) -> Character:
|
|
58
|
+
try:
|
|
59
|
+
character = Character()
|
|
60
|
+
ParseDict(data, character)
|
|
61
|
+
return character
|
|
62
|
+
except Exception as e:
|
|
63
|
+
error_message = str(e)
|
|
64
|
+
raise CharacterValidationError(
|
|
65
|
+
f"Character validation failed: {error_message}",
|
|
66
|
+
errors=[error_message],
|
|
67
|
+
) from e
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def validate_character_config(character: Character) -> dict[str, Any]:
|
|
71
|
+
try:
|
|
72
|
+
# Re-validate by converting to dict and back
|
|
73
|
+
ParseDict(MessageToDict(character, preserving_proto_field_name=False), Character())
|
|
74
|
+
return {
|
|
75
|
+
"isValid": True,
|
|
76
|
+
"errors": [],
|
|
77
|
+
}
|
|
78
|
+
except Exception as e:
|
|
79
|
+
errors = [str(e)]
|
|
80
|
+
return {
|
|
81
|
+
"isValid": False,
|
|
82
|
+
"errors": errors,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def merge_character_defaults(char: dict[str, Any]) -> Character:
|
|
87
|
+
defaults: dict[str, Any] = {
|
|
88
|
+
"settings": {},
|
|
89
|
+
"plugins": [],
|
|
90
|
+
"bio": [],
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
merged = {**defaults, **char}
|
|
94
|
+
if not merged.get("name"):
|
|
95
|
+
merged["name"] = "Unnamed Character"
|
|
96
|
+
|
|
97
|
+
character = Character()
|
|
98
|
+
ParseDict(merged, character)
|
|
99
|
+
return character
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def build_character_plugins(env: dict[str, str | None] | None = None) -> list[str]:
|
|
103
|
+
if env is None:
|
|
104
|
+
env = dict(os.environ)
|
|
105
|
+
|
|
106
|
+
def get_env(key: str) -> str | None:
|
|
107
|
+
value = env.get(key)
|
|
108
|
+
if value:
|
|
109
|
+
return value.strip() if isinstance(value, str) else value
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
plugins: list[str] = ["@elizaos/plugin-sql"]
|
|
113
|
+
if get_env("ANTHROPIC_API_KEY"):
|
|
114
|
+
plugins.append("@elizaos/plugin-anthropic")
|
|
115
|
+
if get_env("OPENROUTER_API_KEY"):
|
|
116
|
+
plugins.append("@elizaos/plugin-openrouter")
|
|
117
|
+
|
|
118
|
+
# Embedding-capable plugins
|
|
119
|
+
if get_env("OPENAI_API_KEY"):
|
|
120
|
+
plugins.append("@elizaos/plugin-openai")
|
|
121
|
+
if get_env("GOOGLE_GENERATIVE_AI_API_KEY"):
|
|
122
|
+
plugins.append("@elizaos/plugin-google-genai")
|
|
123
|
+
if get_env("DISCORD_API_TOKEN"):
|
|
124
|
+
plugins.append("@elizaos/plugin-discord")
|
|
125
|
+
if all(
|
|
126
|
+
get_env(key)
|
|
127
|
+
for key in [
|
|
128
|
+
"X_API_KEY",
|
|
129
|
+
"X_API_SECRET",
|
|
130
|
+
"X_ACCESS_TOKEN",
|
|
131
|
+
"X_ACCESS_TOKEN_SECRET",
|
|
132
|
+
]
|
|
133
|
+
):
|
|
134
|
+
plugins.append("@elizaos/plugin-x")
|
|
135
|
+
if get_env("TELEGRAM_BOT_TOKEN"):
|
|
136
|
+
plugins.append("@elizaos/plugin-telegram")
|
|
137
|
+
has_llm_provider = any(
|
|
138
|
+
get_env(key)
|
|
139
|
+
for key in [
|
|
140
|
+
"ANTHROPIC_API_KEY",
|
|
141
|
+
"OPENROUTER_API_KEY",
|
|
142
|
+
"OPENAI_API_KEY",
|
|
143
|
+
"GOOGLE_GENERATIVE_AI_API_KEY",
|
|
144
|
+
]
|
|
145
|
+
)
|
|
146
|
+
if not has_llm_provider:
|
|
147
|
+
plugins.append("@elizaos/plugin-ollama")
|
|
148
|
+
|
|
149
|
+
return plugins
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import structlog
|
|
9
|
+
|
|
10
|
+
_DEFAULT_REDACT_KEYS: frozenset[str] = frozenset(
|
|
11
|
+
{
|
|
12
|
+
"password",
|
|
13
|
+
"passwd",
|
|
14
|
+
"secret",
|
|
15
|
+
"token",
|
|
16
|
+
"apikey",
|
|
17
|
+
"api_key",
|
|
18
|
+
"apisecret",
|
|
19
|
+
"api_secret",
|
|
20
|
+
"authorization",
|
|
21
|
+
"auth",
|
|
22
|
+
"credential",
|
|
23
|
+
"credentials",
|
|
24
|
+
"privatekey",
|
|
25
|
+
"private_key",
|
|
26
|
+
"accesstoken",
|
|
27
|
+
"access_token",
|
|
28
|
+
"refreshtoken",
|
|
29
|
+
"refresh_token",
|
|
30
|
+
"cookie",
|
|
31
|
+
"session",
|
|
32
|
+
"jwt",
|
|
33
|
+
"bearer",
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _parse_bool(value: str | None, *, default: bool) -> bool:
|
|
39
|
+
if value is None:
|
|
40
|
+
return default
|
|
41
|
+
normalized = value.strip().lower()
|
|
42
|
+
if normalized in {"1", "true", "yes", "on"}:
|
|
43
|
+
return True
|
|
44
|
+
if normalized in {"0", "false", "no", "off"}:
|
|
45
|
+
return False
|
|
46
|
+
return default
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _build_redact_keys() -> frozenset[str]:
|
|
50
|
+
extra = os.environ.get("ELIZA_LOG_REDACT_KEYS", "")
|
|
51
|
+
extra_keys = {k.strip().lower() for k in extra.split(",") if k.strip()}
|
|
52
|
+
return _DEFAULT_REDACT_KEYS.union(extra_keys)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _redact_value(value: object, *, redact_keys: frozenset[str]) -> object:
|
|
56
|
+
if isinstance(value, dict):
|
|
57
|
+
redacted: dict[object, object] = {}
|
|
58
|
+
for k, v in value.items():
|
|
59
|
+
if isinstance(k, str) and k.lower() in redact_keys:
|
|
60
|
+
redacted[k] = "[REDACTED]"
|
|
61
|
+
else:
|
|
62
|
+
redacted[k] = _redact_value(v, redact_keys=redact_keys)
|
|
63
|
+
return redacted
|
|
64
|
+
if isinstance(value, list):
|
|
65
|
+
return [_redact_value(v, redact_keys=redact_keys) for v in value]
|
|
66
|
+
return value
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _redaction_processor(
|
|
70
|
+
_logger: logging.Logger, _method_name: str, event_dict: structlog.types.EventDict
|
|
71
|
+
) -> structlog.types.EventDict:
|
|
72
|
+
enabled = _parse_bool(os.environ.get("ELIZA_LOG_REDACT"), default=True)
|
|
73
|
+
if not enabled:
|
|
74
|
+
return event_dict
|
|
75
|
+
redact_keys = _build_redact_keys()
|
|
76
|
+
return _redact_value(event_dict, redact_keys=redact_keys) # type: ignore[return-value]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def configure_structlog(log_level: str = "INFO") -> None:
|
|
80
|
+
json_mode = _parse_bool(os.environ.get("LOG_JSON_FORMAT"), default=False)
|
|
81
|
+
show_timestamps = _parse_bool(os.environ.get("LOG_TIMESTAMPS"), default=True)
|
|
82
|
+
|
|
83
|
+
logging.basicConfig(
|
|
84
|
+
format="%(message)s",
|
|
85
|
+
stream=sys.stdout,
|
|
86
|
+
level=getattr(logging, log_level.upper(), logging.INFO),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
base_processors: list[structlog.types.Processor] = [
|
|
90
|
+
structlog.stdlib.filter_by_level,
|
|
91
|
+
structlog.stdlib.add_logger_name,
|
|
92
|
+
structlog.stdlib.add_log_level,
|
|
93
|
+
structlog.stdlib.PositionalArgumentsFormatter(),
|
|
94
|
+
_redaction_processor,
|
|
95
|
+
]
|
|
96
|
+
if show_timestamps:
|
|
97
|
+
base_processors.append(structlog.processors.TimeStamper(fmt="iso"))
|
|
98
|
+
base_processors.extend(
|
|
99
|
+
[
|
|
100
|
+
structlog.processors.StackInfoRenderer(),
|
|
101
|
+
structlog.processors.format_exc_info,
|
|
102
|
+
structlog.processors.UnicodeDecoder(),
|
|
103
|
+
]
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
structlog.configure(
|
|
107
|
+
processors=[
|
|
108
|
+
*base_processors,
|
|
109
|
+
structlog.processors.JSONRenderer()
|
|
110
|
+
if json_mode
|
|
111
|
+
else structlog.dev.ConsoleRenderer(colors=True),
|
|
112
|
+
],
|
|
113
|
+
wrapper_class=structlog.stdlib.BoundLogger,
|
|
114
|
+
context_class=dict,
|
|
115
|
+
logger_factory=structlog.stdlib.LoggerFactory(),
|
|
116
|
+
cache_logger_on_first_use=True,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class Logger:
|
|
121
|
+
def __init__(
|
|
122
|
+
self,
|
|
123
|
+
namespace: str | None = None,
|
|
124
|
+
level: str = "INFO",
|
|
125
|
+
) -> None:
|
|
126
|
+
self._namespace = namespace or "elizaos"
|
|
127
|
+
self._level = level
|
|
128
|
+
self._logger = structlog.get_logger(self._namespace)
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def namespace(self) -> str:
|
|
132
|
+
return self._namespace
|
|
133
|
+
|
|
134
|
+
def _log(
|
|
135
|
+
self,
|
|
136
|
+
level: str,
|
|
137
|
+
message: str,
|
|
138
|
+
*args: Any,
|
|
139
|
+
**kwargs: Any,
|
|
140
|
+
) -> None:
|
|
141
|
+
log_method = getattr(self._logger, level.lower(), self._logger.info)
|
|
142
|
+
if args:
|
|
143
|
+
log_method(message, *args, **kwargs)
|
|
144
|
+
else:
|
|
145
|
+
log_method(message, **kwargs)
|
|
146
|
+
|
|
147
|
+
def debug(self, message: str, *args: Any, **kwargs: Any) -> None:
|
|
148
|
+
self._log("debug", message, *args, **kwargs)
|
|
149
|
+
|
|
150
|
+
def info(self, message: str, *args: Any, **kwargs: Any) -> None:
|
|
151
|
+
self._log("info", message, *args, **kwargs)
|
|
152
|
+
|
|
153
|
+
def warn(self, message: str, *args: Any, **kwargs: Any) -> None:
|
|
154
|
+
self._log("warning", message, *args, **kwargs)
|
|
155
|
+
|
|
156
|
+
def warning(self, message: str, *args: Any, **kwargs: Any) -> None:
|
|
157
|
+
self._log("warning", message, *args, **kwargs)
|
|
158
|
+
|
|
159
|
+
def error(self, message: str, *args: Any, **kwargs: Any) -> None:
|
|
160
|
+
self._log("error", message, *args, **kwargs)
|
|
161
|
+
|
|
162
|
+
def exception(self, message: str, *args: Any, **kwargs: Any) -> None:
|
|
163
|
+
self._log("exception", message, *args, exc_info=True, **kwargs)
|
|
164
|
+
|
|
165
|
+
def bind(self, **kwargs: Any) -> Logger:
|
|
166
|
+
new_logger = Logger(namespace=self._namespace, level=self._level)
|
|
167
|
+
new_logger._logger = self._logger.bind(**kwargs)
|
|
168
|
+
return new_logger
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def create_logger(
|
|
172
|
+
namespace: str | None = None,
|
|
173
|
+
level: str = "INFO",
|
|
174
|
+
) -> Logger:
|
|
175
|
+
return Logger(namespace=namespace, level=level)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
configure_structlog(log_level=os.environ.get("LOG_LEVEL", "INFO"))
|
|
179
|
+
logger = create_logger()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Media utilities for Eliza.
|
|
3
|
+
|
|
4
|
+
Provides MIME type detection, media parsing, format utilities, and hybrid search.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .mime import (
|
|
8
|
+
MediaKind,
|
|
9
|
+
detect_mime,
|
|
10
|
+
extension_for_mime,
|
|
11
|
+
get_file_extension,
|
|
12
|
+
image_mime_from_format,
|
|
13
|
+
is_audio_filename,
|
|
14
|
+
is_gif_media,
|
|
15
|
+
is_voice_compatible_audio,
|
|
16
|
+
media_kind_from_mime,
|
|
17
|
+
)
|
|
18
|
+
from .search import (
|
|
19
|
+
HybridKeywordResult,
|
|
20
|
+
HybridMergedResult,
|
|
21
|
+
HybridVectorResult,
|
|
22
|
+
bm25_rank_to_score,
|
|
23
|
+
build_fts_query,
|
|
24
|
+
merge_hybrid_results,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
# MIME utilities
|
|
29
|
+
"MediaKind",
|
|
30
|
+
"detect_mime",
|
|
31
|
+
"extension_for_mime",
|
|
32
|
+
"get_file_extension",
|
|
33
|
+
"image_mime_from_format",
|
|
34
|
+
"is_audio_filename",
|
|
35
|
+
"is_gif_media",
|
|
36
|
+
"is_voice_compatible_audio",
|
|
37
|
+
"media_kind_from_mime",
|
|
38
|
+
# Hybrid search utilities
|
|
39
|
+
"HybridKeywordResult",
|
|
40
|
+
"HybridMergedResult",
|
|
41
|
+
"HybridVectorResult",
|
|
42
|
+
"bm25_rank_to_score",
|
|
43
|
+
"build_fts_query",
|
|
44
|
+
"merge_hybrid_results",
|
|
45
|
+
]
|