@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.
Files changed (197) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +239 -0
  3. package/elizaos/__init__.py +280 -0
  4. package/elizaos/action_docs.py +149 -0
  5. package/elizaos/advanced_capabilities/__init__.py +85 -0
  6. package/elizaos/advanced_capabilities/actions/__init__.py +54 -0
  7. package/elizaos/advanced_capabilities/actions/add_contact.py +139 -0
  8. package/elizaos/advanced_capabilities/actions/follow_room.py +151 -0
  9. package/elizaos/advanced_capabilities/actions/image_generation.py +148 -0
  10. package/elizaos/advanced_capabilities/actions/mute_room.py +164 -0
  11. package/elizaos/advanced_capabilities/actions/remove_contact.py +145 -0
  12. package/elizaos/advanced_capabilities/actions/roles.py +207 -0
  13. package/elizaos/advanced_capabilities/actions/schedule_follow_up.py +154 -0
  14. package/elizaos/advanced_capabilities/actions/search_contacts.py +145 -0
  15. package/elizaos/advanced_capabilities/actions/send_message.py +187 -0
  16. package/elizaos/advanced_capabilities/actions/settings.py +151 -0
  17. package/elizaos/advanced_capabilities/actions/unfollow_room.py +164 -0
  18. package/elizaos/advanced_capabilities/actions/unmute_room.py +164 -0
  19. package/elizaos/advanced_capabilities/actions/update_contact.py +164 -0
  20. package/elizaos/advanced_capabilities/actions/update_entity.py +161 -0
  21. package/elizaos/advanced_capabilities/evaluators/__init__.py +18 -0
  22. package/elizaos/advanced_capabilities/evaluators/reflection.py +134 -0
  23. package/elizaos/advanced_capabilities/evaluators/relationship_extraction.py +203 -0
  24. package/elizaos/advanced_capabilities/providers/__init__.py +36 -0
  25. package/elizaos/advanced_capabilities/providers/agent_settings.py +60 -0
  26. package/elizaos/advanced_capabilities/providers/contacts.py +77 -0
  27. package/elizaos/advanced_capabilities/providers/facts.py +82 -0
  28. package/elizaos/advanced_capabilities/providers/follow_ups.py +113 -0
  29. package/elizaos/advanced_capabilities/providers/knowledge.py +83 -0
  30. package/elizaos/advanced_capabilities/providers/relationships.py +112 -0
  31. package/elizaos/advanced_capabilities/providers/roles.py +97 -0
  32. package/elizaos/advanced_capabilities/providers/settings.py +51 -0
  33. package/elizaos/advanced_capabilities/services/__init__.py +18 -0
  34. package/elizaos/advanced_capabilities/services/follow_up.py +138 -0
  35. package/elizaos/advanced_capabilities/services/rolodex.py +244 -0
  36. package/elizaos/advanced_memory/__init__.py +3 -0
  37. package/elizaos/advanced_memory/evaluators.py +97 -0
  38. package/elizaos/advanced_memory/memory_service.py +556 -0
  39. package/elizaos/advanced_memory/plugin.py +30 -0
  40. package/elizaos/advanced_memory/prompts.py +12 -0
  41. package/elizaos/advanced_memory/providers.py +90 -0
  42. package/elizaos/advanced_memory/types.py +65 -0
  43. package/elizaos/advanced_planning/__init__.py +10 -0
  44. package/elizaos/advanced_planning/actions.py +145 -0
  45. package/elizaos/advanced_planning/message_classifier.py +127 -0
  46. package/elizaos/advanced_planning/planning_service.py +712 -0
  47. package/elizaos/advanced_planning/plugin.py +40 -0
  48. package/elizaos/advanced_planning/prompts.py +4 -0
  49. package/elizaos/basic_capabilities/__init__.py +66 -0
  50. package/elizaos/basic_capabilities/actions/__init__.py +24 -0
  51. package/elizaos/basic_capabilities/actions/choice.py +140 -0
  52. package/elizaos/basic_capabilities/actions/ignore.py +66 -0
  53. package/elizaos/basic_capabilities/actions/none.py +56 -0
  54. package/elizaos/basic_capabilities/actions/reply.py +120 -0
  55. package/elizaos/basic_capabilities/providers/__init__.py +54 -0
  56. package/elizaos/basic_capabilities/providers/action_state.py +113 -0
  57. package/elizaos/basic_capabilities/providers/actions.py +263 -0
  58. package/elizaos/basic_capabilities/providers/attachments.py +76 -0
  59. package/elizaos/basic_capabilities/providers/capabilities.py +62 -0
  60. package/elizaos/basic_capabilities/providers/character.py +113 -0
  61. package/elizaos/basic_capabilities/providers/choice.py +73 -0
  62. package/elizaos/basic_capabilities/providers/context_bench.py +44 -0
  63. package/elizaos/basic_capabilities/providers/current_time.py +58 -0
  64. package/elizaos/basic_capabilities/providers/entities.py +99 -0
  65. package/elizaos/basic_capabilities/providers/evaluators.py +54 -0
  66. package/elizaos/basic_capabilities/providers/providers_list.py +55 -0
  67. package/elizaos/basic_capabilities/providers/recent_messages.py +85 -0
  68. package/elizaos/basic_capabilities/providers/time.py +45 -0
  69. package/elizaos/basic_capabilities/providers/world.py +93 -0
  70. package/elizaos/basic_capabilities/services/__init__.py +18 -0
  71. package/elizaos/basic_capabilities/services/embedding.py +122 -0
  72. package/elizaos/basic_capabilities/services/task.py +178 -0
  73. package/elizaos/bootstrap/__init__.py +12 -0
  74. package/elizaos/bootstrap/actions/__init__.py +68 -0
  75. package/elizaos/bootstrap/actions/add_contact.py +149 -0
  76. package/elizaos/bootstrap/actions/choice.py +147 -0
  77. package/elizaos/bootstrap/actions/follow_room.py +151 -0
  78. package/elizaos/bootstrap/actions/ignore.py +80 -0
  79. package/elizaos/bootstrap/actions/image_generation.py +135 -0
  80. package/elizaos/bootstrap/actions/mute_room.py +151 -0
  81. package/elizaos/bootstrap/actions/none.py +71 -0
  82. package/elizaos/bootstrap/actions/remove_contact.py +159 -0
  83. package/elizaos/bootstrap/actions/reply.py +140 -0
  84. package/elizaos/bootstrap/actions/roles.py +193 -0
  85. package/elizaos/bootstrap/actions/schedule_follow_up.py +164 -0
  86. package/elizaos/bootstrap/actions/search_contacts.py +159 -0
  87. package/elizaos/bootstrap/actions/send_message.py +173 -0
  88. package/elizaos/bootstrap/actions/settings.py +165 -0
  89. package/elizaos/bootstrap/actions/unfollow_room.py +151 -0
  90. package/elizaos/bootstrap/actions/unmute_room.py +151 -0
  91. package/elizaos/bootstrap/actions/update_contact.py +178 -0
  92. package/elizaos/bootstrap/actions/update_entity.py +175 -0
  93. package/elizaos/bootstrap/autonomy/__init__.py +18 -0
  94. package/elizaos/bootstrap/autonomy/action.py +197 -0
  95. package/elizaos/bootstrap/autonomy/providers.py +165 -0
  96. package/elizaos/bootstrap/autonomy/routes.py +171 -0
  97. package/elizaos/bootstrap/autonomy/service.py +562 -0
  98. package/elizaos/bootstrap/autonomy/types.py +18 -0
  99. package/elizaos/bootstrap/evaluators/__init__.py +19 -0
  100. package/elizaos/bootstrap/evaluators/reflection.py +118 -0
  101. package/elizaos/bootstrap/evaluators/relationship_extraction.py +192 -0
  102. package/elizaos/bootstrap/plugin.py +140 -0
  103. package/elizaos/bootstrap/providers/__init__.py +80 -0
  104. package/elizaos/bootstrap/providers/action_state.py +71 -0
  105. package/elizaos/bootstrap/providers/actions.py +256 -0
  106. package/elizaos/bootstrap/providers/agent_settings.py +63 -0
  107. package/elizaos/bootstrap/providers/attachments.py +76 -0
  108. package/elizaos/bootstrap/providers/capabilities.py +66 -0
  109. package/elizaos/bootstrap/providers/character.py +128 -0
  110. package/elizaos/bootstrap/providers/choice.py +77 -0
  111. package/elizaos/bootstrap/providers/contacts.py +78 -0
  112. package/elizaos/bootstrap/providers/context_bench.py +49 -0
  113. package/elizaos/bootstrap/providers/current_time.py +56 -0
  114. package/elizaos/bootstrap/providers/entities.py +99 -0
  115. package/elizaos/bootstrap/providers/evaluators.py +58 -0
  116. package/elizaos/bootstrap/providers/facts.py +86 -0
  117. package/elizaos/bootstrap/providers/follow_ups.py +116 -0
  118. package/elizaos/bootstrap/providers/knowledge.py +73 -0
  119. package/elizaos/bootstrap/providers/providers_list.py +59 -0
  120. package/elizaos/bootstrap/providers/recent_messages.py +85 -0
  121. package/elizaos/bootstrap/providers/relationships.py +106 -0
  122. package/elizaos/bootstrap/providers/roles.py +95 -0
  123. package/elizaos/bootstrap/providers/settings.py +55 -0
  124. package/elizaos/bootstrap/providers/time.py +45 -0
  125. package/elizaos/bootstrap/providers/world.py +97 -0
  126. package/elizaos/bootstrap/services/__init__.py +26 -0
  127. package/elizaos/bootstrap/services/embedding.py +122 -0
  128. package/elizaos/bootstrap/services/follow_up.py +138 -0
  129. package/elizaos/bootstrap/services/rolodex.py +244 -0
  130. package/elizaos/bootstrap/services/task.py +585 -0
  131. package/elizaos/bootstrap/types.py +54 -0
  132. package/elizaos/bootstrap/utils/__init__.py +7 -0
  133. package/elizaos/bootstrap/utils/xml.py +69 -0
  134. package/elizaos/character.py +149 -0
  135. package/elizaos/logger.py +179 -0
  136. package/elizaos/media/__init__.py +45 -0
  137. package/elizaos/media/mime.py +315 -0
  138. package/elizaos/media/search.py +161 -0
  139. package/elizaos/media/tests/__init__.py +1 -0
  140. package/elizaos/media/tests/test_mime.py +117 -0
  141. package/elizaos/media/tests/test_search.py +156 -0
  142. package/elizaos/plugin.py +191 -0
  143. package/elizaos/prompts.py +1071 -0
  144. package/elizaos/py.typed +0 -0
  145. package/elizaos/runtime.py +2572 -0
  146. package/elizaos/services/__init__.py +49 -0
  147. package/elizaos/services/hook_service.py +511 -0
  148. package/elizaos/services/message_service.py +1248 -0
  149. package/elizaos/settings.py +182 -0
  150. package/elizaos/streaming_context.py +159 -0
  151. package/elizaos/trajectory_context.py +18 -0
  152. package/elizaos/types/__init__.py +512 -0
  153. package/elizaos/types/agent.py +31 -0
  154. package/elizaos/types/components.py +208 -0
  155. package/elizaos/types/database.py +64 -0
  156. package/elizaos/types/environment.py +46 -0
  157. package/elizaos/types/events.py +47 -0
  158. package/elizaos/types/memory.py +45 -0
  159. package/elizaos/types/model.py +393 -0
  160. package/elizaos/types/plugin.py +188 -0
  161. package/elizaos/types/primitives.py +100 -0
  162. package/elizaos/types/runtime.py +460 -0
  163. package/elizaos/types/service.py +113 -0
  164. package/elizaos/types/service_interfaces.py +244 -0
  165. package/elizaos/types/state.py +188 -0
  166. package/elizaos/types/task.py +29 -0
  167. package/elizaos/utils/__init__.py +108 -0
  168. package/elizaos/utils/spec_examples.py +48 -0
  169. package/elizaos/utils/streaming.py +426 -0
  170. package/elizaos_atropos_shared/__init__.py +1 -0
  171. package/elizaos_atropos_shared/canonical_eliza.py +282 -0
  172. package/package.json +19 -0
  173. package/pyproject.toml +143 -0
  174. package/requirements-dev.in +11 -0
  175. package/requirements-dev.lock +134 -0
  176. package/requirements.in +9 -0
  177. package/requirements.lock +64 -0
  178. package/tests/__init__.py +0 -0
  179. package/tests/test_action_parameters.py +154 -0
  180. package/tests/test_actions_provider_examples.py +39 -0
  181. package/tests/test_advanced_memory_behavior.py +96 -0
  182. package/tests/test_advanced_memory_flag.py +30 -0
  183. package/tests/test_advanced_planning_behavior.py +225 -0
  184. package/tests/test_advanced_planning_flag.py +26 -0
  185. package/tests/test_autonomy.py +445 -0
  186. package/tests/test_bootstrap_initialize.py +37 -0
  187. package/tests/test_character.py +163 -0
  188. package/tests/test_character_provider.py +231 -0
  189. package/tests/test_dynamic_prompt_exec.py +561 -0
  190. package/tests/test_logger_redaction.py +43 -0
  191. package/tests/test_plugin.py +117 -0
  192. package/tests/test_runtime.py +422 -0
  193. package/tests/test_salt_production_enforcement.py +22 -0
  194. package/tests/test_settings_crypto.py +118 -0
  195. package/tests/test_streaming.py +295 -0
  196. package/tests/test_types.py +221 -0
  197. 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
+ ]