kweaver-dolphin 0.1.0__py3-none-any.whl

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 (199) hide show
  1. DolphinLanguageSDK/__init__.py +58 -0
  2. dolphin/__init__.py +62 -0
  3. dolphin/cli/__init__.py +20 -0
  4. dolphin/cli/args/__init__.py +9 -0
  5. dolphin/cli/args/parser.py +567 -0
  6. dolphin/cli/builtin_agents/__init__.py +22 -0
  7. dolphin/cli/commands/__init__.py +4 -0
  8. dolphin/cli/interrupt/__init__.py +8 -0
  9. dolphin/cli/interrupt/handler.py +205 -0
  10. dolphin/cli/interrupt/keyboard.py +82 -0
  11. dolphin/cli/main.py +49 -0
  12. dolphin/cli/multimodal/__init__.py +34 -0
  13. dolphin/cli/multimodal/clipboard.py +327 -0
  14. dolphin/cli/multimodal/handler.py +249 -0
  15. dolphin/cli/multimodal/image_processor.py +214 -0
  16. dolphin/cli/multimodal/input_parser.py +149 -0
  17. dolphin/cli/runner/__init__.py +8 -0
  18. dolphin/cli/runner/runner.py +989 -0
  19. dolphin/cli/ui/__init__.py +10 -0
  20. dolphin/cli/ui/console.py +2795 -0
  21. dolphin/cli/ui/input.py +340 -0
  22. dolphin/cli/ui/layout.py +425 -0
  23. dolphin/cli/ui/stream_renderer.py +302 -0
  24. dolphin/cli/utils/__init__.py +8 -0
  25. dolphin/cli/utils/helpers.py +135 -0
  26. dolphin/cli/utils/version.py +49 -0
  27. dolphin/core/__init__.py +107 -0
  28. dolphin/core/agent/__init__.py +10 -0
  29. dolphin/core/agent/agent_state.py +69 -0
  30. dolphin/core/agent/base_agent.py +970 -0
  31. dolphin/core/code_block/__init__.py +0 -0
  32. dolphin/core/code_block/agent_init_block.py +0 -0
  33. dolphin/core/code_block/assign_block.py +98 -0
  34. dolphin/core/code_block/basic_code_block.py +1865 -0
  35. dolphin/core/code_block/explore_block.py +1327 -0
  36. dolphin/core/code_block/explore_block_v2.py +712 -0
  37. dolphin/core/code_block/explore_strategy.py +672 -0
  38. dolphin/core/code_block/judge_block.py +220 -0
  39. dolphin/core/code_block/prompt_block.py +32 -0
  40. dolphin/core/code_block/skill_call_deduplicator.py +291 -0
  41. dolphin/core/code_block/tool_block.py +129 -0
  42. dolphin/core/common/__init__.py +17 -0
  43. dolphin/core/common/constants.py +176 -0
  44. dolphin/core/common/enums.py +1173 -0
  45. dolphin/core/common/exceptions.py +133 -0
  46. dolphin/core/common/multimodal.py +539 -0
  47. dolphin/core/common/object_type.py +165 -0
  48. dolphin/core/common/output_format.py +432 -0
  49. dolphin/core/common/types.py +36 -0
  50. dolphin/core/config/__init__.py +16 -0
  51. dolphin/core/config/global_config.py +1289 -0
  52. dolphin/core/config/ontology_config.py +133 -0
  53. dolphin/core/context/__init__.py +12 -0
  54. dolphin/core/context/context.py +1580 -0
  55. dolphin/core/context/context_manager.py +161 -0
  56. dolphin/core/context/var_output.py +82 -0
  57. dolphin/core/context/variable_pool.py +356 -0
  58. dolphin/core/context_engineer/__init__.py +41 -0
  59. dolphin/core/context_engineer/config/__init__.py +5 -0
  60. dolphin/core/context_engineer/config/settings.py +402 -0
  61. dolphin/core/context_engineer/core/__init__.py +7 -0
  62. dolphin/core/context_engineer/core/budget_manager.py +327 -0
  63. dolphin/core/context_engineer/core/context_assembler.py +583 -0
  64. dolphin/core/context_engineer/core/context_manager.py +637 -0
  65. dolphin/core/context_engineer/core/tokenizer_service.py +260 -0
  66. dolphin/core/context_engineer/example/incremental_example.py +267 -0
  67. dolphin/core/context_engineer/example/traditional_example.py +334 -0
  68. dolphin/core/context_engineer/services/__init__.py +5 -0
  69. dolphin/core/context_engineer/services/compressor.py +399 -0
  70. dolphin/core/context_engineer/utils/__init__.py +6 -0
  71. dolphin/core/context_engineer/utils/context_utils.py +441 -0
  72. dolphin/core/context_engineer/utils/message_formatter.py +270 -0
  73. dolphin/core/context_engineer/utils/token_utils.py +139 -0
  74. dolphin/core/coroutine/__init__.py +15 -0
  75. dolphin/core/coroutine/context_snapshot.py +154 -0
  76. dolphin/core/coroutine/context_snapshot_profile.py +922 -0
  77. dolphin/core/coroutine/context_snapshot_store.py +268 -0
  78. dolphin/core/coroutine/execution_frame.py +145 -0
  79. dolphin/core/coroutine/execution_state_registry.py +161 -0
  80. dolphin/core/coroutine/resume_handle.py +101 -0
  81. dolphin/core/coroutine/step_result.py +101 -0
  82. dolphin/core/executor/__init__.py +18 -0
  83. dolphin/core/executor/debug_controller.py +630 -0
  84. dolphin/core/executor/dolphin_executor.py +1063 -0
  85. dolphin/core/executor/executor.py +624 -0
  86. dolphin/core/flags/__init__.py +27 -0
  87. dolphin/core/flags/definitions.py +49 -0
  88. dolphin/core/flags/manager.py +113 -0
  89. dolphin/core/hook/__init__.py +95 -0
  90. dolphin/core/hook/expression_evaluator.py +499 -0
  91. dolphin/core/hook/hook_dispatcher.py +380 -0
  92. dolphin/core/hook/hook_types.py +248 -0
  93. dolphin/core/hook/isolated_variable_pool.py +284 -0
  94. dolphin/core/interfaces.py +53 -0
  95. dolphin/core/llm/__init__.py +0 -0
  96. dolphin/core/llm/llm.py +495 -0
  97. dolphin/core/llm/llm_call.py +100 -0
  98. dolphin/core/llm/llm_client.py +1285 -0
  99. dolphin/core/llm/message_sanitizer.py +120 -0
  100. dolphin/core/logging/__init__.py +20 -0
  101. dolphin/core/logging/logger.py +526 -0
  102. dolphin/core/message/__init__.py +8 -0
  103. dolphin/core/message/compressor.py +749 -0
  104. dolphin/core/parser/__init__.py +8 -0
  105. dolphin/core/parser/parser.py +405 -0
  106. dolphin/core/runtime/__init__.py +10 -0
  107. dolphin/core/runtime/runtime_graph.py +926 -0
  108. dolphin/core/runtime/runtime_instance.py +446 -0
  109. dolphin/core/skill/__init__.py +14 -0
  110. dolphin/core/skill/context_retention.py +157 -0
  111. dolphin/core/skill/skill_function.py +686 -0
  112. dolphin/core/skill/skill_matcher.py +282 -0
  113. dolphin/core/skill/skillkit.py +700 -0
  114. dolphin/core/skill/skillset.py +72 -0
  115. dolphin/core/trajectory/__init__.py +10 -0
  116. dolphin/core/trajectory/recorder.py +189 -0
  117. dolphin/core/trajectory/trajectory.py +522 -0
  118. dolphin/core/utils/__init__.py +9 -0
  119. dolphin/core/utils/cache_kv.py +212 -0
  120. dolphin/core/utils/tools.py +340 -0
  121. dolphin/lib/__init__.py +93 -0
  122. dolphin/lib/debug/__init__.py +8 -0
  123. dolphin/lib/debug/visualizer.py +409 -0
  124. dolphin/lib/memory/__init__.py +28 -0
  125. dolphin/lib/memory/async_processor.py +220 -0
  126. dolphin/lib/memory/llm_calls.py +195 -0
  127. dolphin/lib/memory/manager.py +78 -0
  128. dolphin/lib/memory/sandbox.py +46 -0
  129. dolphin/lib/memory/storage.py +245 -0
  130. dolphin/lib/memory/utils.py +51 -0
  131. dolphin/lib/ontology/__init__.py +12 -0
  132. dolphin/lib/ontology/basic/__init__.py +0 -0
  133. dolphin/lib/ontology/basic/base.py +102 -0
  134. dolphin/lib/ontology/basic/concept.py +130 -0
  135. dolphin/lib/ontology/basic/object.py +11 -0
  136. dolphin/lib/ontology/basic/relation.py +63 -0
  137. dolphin/lib/ontology/datasource/__init__.py +27 -0
  138. dolphin/lib/ontology/datasource/datasource.py +66 -0
  139. dolphin/lib/ontology/datasource/oracle_datasource.py +338 -0
  140. dolphin/lib/ontology/datasource/sql.py +845 -0
  141. dolphin/lib/ontology/mapping.py +177 -0
  142. dolphin/lib/ontology/ontology.py +733 -0
  143. dolphin/lib/ontology/ontology_context.py +16 -0
  144. dolphin/lib/ontology/ontology_manager.py +107 -0
  145. dolphin/lib/skill_results/__init__.py +31 -0
  146. dolphin/lib/skill_results/cache_backend.py +559 -0
  147. dolphin/lib/skill_results/result_processor.py +181 -0
  148. dolphin/lib/skill_results/result_reference.py +179 -0
  149. dolphin/lib/skill_results/skillkit_hook.py +324 -0
  150. dolphin/lib/skill_results/strategies.py +328 -0
  151. dolphin/lib/skill_results/strategy_registry.py +150 -0
  152. dolphin/lib/skillkits/__init__.py +44 -0
  153. dolphin/lib/skillkits/agent_skillkit.py +155 -0
  154. dolphin/lib/skillkits/cognitive_skillkit.py +82 -0
  155. dolphin/lib/skillkits/env_skillkit.py +250 -0
  156. dolphin/lib/skillkits/mcp_adapter.py +616 -0
  157. dolphin/lib/skillkits/mcp_skillkit.py +771 -0
  158. dolphin/lib/skillkits/memory_skillkit.py +650 -0
  159. dolphin/lib/skillkits/noop_skillkit.py +31 -0
  160. dolphin/lib/skillkits/ontology_skillkit.py +89 -0
  161. dolphin/lib/skillkits/plan_act_skillkit.py +452 -0
  162. dolphin/lib/skillkits/resource/__init__.py +52 -0
  163. dolphin/lib/skillkits/resource/models/__init__.py +6 -0
  164. dolphin/lib/skillkits/resource/models/skill_config.py +109 -0
  165. dolphin/lib/skillkits/resource/models/skill_meta.py +127 -0
  166. dolphin/lib/skillkits/resource/resource_skillkit.py +393 -0
  167. dolphin/lib/skillkits/resource/skill_cache.py +215 -0
  168. dolphin/lib/skillkits/resource/skill_loader.py +395 -0
  169. dolphin/lib/skillkits/resource/skill_validator.py +406 -0
  170. dolphin/lib/skillkits/resource_skillkit.py +11 -0
  171. dolphin/lib/skillkits/search_skillkit.py +163 -0
  172. dolphin/lib/skillkits/sql_skillkit.py +274 -0
  173. dolphin/lib/skillkits/system_skillkit.py +509 -0
  174. dolphin/lib/skillkits/vm_skillkit.py +65 -0
  175. dolphin/lib/utils/__init__.py +9 -0
  176. dolphin/lib/utils/data_process.py +207 -0
  177. dolphin/lib/utils/handle_progress.py +178 -0
  178. dolphin/lib/utils/security.py +139 -0
  179. dolphin/lib/utils/text_retrieval.py +462 -0
  180. dolphin/lib/vm/__init__.py +11 -0
  181. dolphin/lib/vm/env_executor.py +895 -0
  182. dolphin/lib/vm/python_session_manager.py +453 -0
  183. dolphin/lib/vm/vm.py +610 -0
  184. dolphin/sdk/__init__.py +60 -0
  185. dolphin/sdk/agent/__init__.py +12 -0
  186. dolphin/sdk/agent/agent_factory.py +236 -0
  187. dolphin/sdk/agent/dolphin_agent.py +1106 -0
  188. dolphin/sdk/api/__init__.py +4 -0
  189. dolphin/sdk/runtime/__init__.py +8 -0
  190. dolphin/sdk/runtime/env.py +363 -0
  191. dolphin/sdk/skill/__init__.py +10 -0
  192. dolphin/sdk/skill/global_skills.py +706 -0
  193. dolphin/sdk/skill/traditional_toolkit.py +260 -0
  194. kweaver_dolphin-0.1.0.dist-info/METADATA +521 -0
  195. kweaver_dolphin-0.1.0.dist-info/RECORD +199 -0
  196. kweaver_dolphin-0.1.0.dist-info/WHEEL +5 -0
  197. kweaver_dolphin-0.1.0.dist-info/entry_points.txt +27 -0
  198. kweaver_dolphin-0.1.0.dist-info/licenses/LICENSE.txt +201 -0
  199. kweaver_dolphin-0.1.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,120 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable, Dict, List, Optional, Tuple
4
+
5
+ from dolphin.core.common.constants import PIN_MARKER
6
+
7
+ # Default prefix for downgraded tool messages
8
+ DOWNGRADED_TOOL_PREFIX = "[Tool Output]: "
9
+
10
+
11
+ def sanitize_openai_messages(
12
+ messages: List[Dict[str, Any]],
13
+ *,
14
+ downgrade_role: str = "user",
15
+ pinned_downgrade_role: str = "assistant",
16
+ downgraded_prefix: str = DOWNGRADED_TOOL_PREFIX,
17
+ ) -> Tuple[List[Dict[str, Any]], int]:
18
+ """Best-effort sanitizer for OpenAI-compatible tool message constraints.
19
+
20
+ OpenAI-compatible APIs require:
21
+ - role="tool" messages must be responses to a preceding assistant message with tool_calls
22
+ - tool messages must carry tool_call_id and it must match a previously declared tool_calls[].id
23
+
24
+ In real systems, context restore/compression/persistence may produce orphan tool messages, e.g.:
25
+ - tool appears before any assistant(tool_calls) (system -> tool -> user)
26
+ - tool_call_id missing or mismatched
27
+
28
+ This function downgrades orphan tool messages into normal text messages to avoid hard API failures.
29
+
30
+ Returns:
31
+ - sanitized_messages: list[dict] safe to send to OpenAI-compatible APIs
32
+ - downgraded_count: number of tool messages downgraded
33
+ """
34
+ if not messages:
35
+ return messages, 0
36
+
37
+ declared_tool_call_ids: set[str] = set()
38
+ seen_any_tool_calls = False
39
+ downgraded = 0
40
+ sanitized: List[Dict[str, Any]] = []
41
+
42
+ for msg in messages:
43
+ role = msg.get("role")
44
+
45
+ if role == "assistant":
46
+ tool_calls = msg.get("tool_calls") or []
47
+ if isinstance(tool_calls, list) and tool_calls:
48
+ seen_any_tool_calls = True
49
+ for tc in tool_calls:
50
+ if isinstance(tc, dict) and tc.get("id"):
51
+ declared_tool_call_ids.add(tc["id"])
52
+ sanitized.append(msg)
53
+ continue
54
+
55
+ if role != "tool":
56
+ sanitized.append(msg)
57
+ continue
58
+
59
+ # NOTE: Some workflows inject pinned tool outputs (e.g. skill docs) even when
60
+ # the model didn't initiate a tool call. OpenAI-compatible providers (incl. DashScope)
61
+ # may reject *any* role="tool" message unless it is part of a valid tool_calls sequence.
62
+ # Pinned outputs are best represented as normal assistant text to avoid hard API failures.
63
+ content = msg.get("content") or ""
64
+ if not isinstance(content, str):
65
+ content = str(content)
66
+
67
+ tool_call_id = msg.get("tool_call_id")
68
+ is_orphan = False
69
+ if not seen_any_tool_calls:
70
+ is_orphan = True
71
+ elif not tool_call_id or not isinstance(tool_call_id, str):
72
+ is_orphan = True
73
+ elif tool_call_id not in declared_tool_call_ids:
74
+ is_orphan = True
75
+
76
+ # If a pinned tool message is a valid response to a preceding tool_calls entry,
77
+ # keep it as role="tool" so OpenAI-compatible providers won't reject the sequence.
78
+ if content.startswith(PIN_MARKER):
79
+ if is_orphan:
80
+ downgraded += 1
81
+ sanitized.append({"role": pinned_downgrade_role, "content": content})
82
+ else:
83
+ sanitized.append(msg)
84
+ continue
85
+
86
+ if not is_orphan:
87
+ sanitized.append(msg)
88
+ continue
89
+
90
+ downgraded += 1
91
+ downgraded_msg = {"role": downgrade_role, "content": f"{downgraded_prefix}{content}"}
92
+ sanitized.append(downgraded_msg)
93
+
94
+ return sanitized, downgraded
95
+
96
+
97
+ def sanitize_and_log(
98
+ messages: List[Dict[str, Any]],
99
+ warning_fn: Optional[Callable[[str], None]] = None,
100
+ **sanitize_kwargs,
101
+ ) -> List[Dict[str, Any]]:
102
+ """Sanitize messages and optionally log warnings about downgraded tool messages.
103
+
104
+ Args:
105
+ messages: List of message dictionaries to sanitize
106
+ warning_fn: Optional callable to log warnings (e.g., logger.warning or context.warn)
107
+ **sanitize_kwargs: Additional arguments to pass to sanitize_openai_messages
108
+
109
+ Returns:
110
+ Sanitized message list safe for OpenAI-compatible APIs
111
+ """
112
+ sanitized_messages, downgraded_count = sanitize_openai_messages(
113
+ messages, **sanitize_kwargs
114
+ )
115
+ if downgraded_count and warning_fn:
116
+ warning_fn(
117
+ f"Detected {downgraded_count} orphan tool message(s); "
118
+ "downgraded for OpenAI compatibility"
119
+ )
120
+ return sanitized_messages
@@ -0,0 +1,20 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Logging ๆจกๅ— - ๆ—ฅๅฟ—็ณป็ปŸ"""
3
+
4
+ from dolphin.core.logging.logger import (
5
+ get_logger,
6
+ console,
7
+ setup_logger,
8
+ set_log_level,
9
+ Colors,
10
+ colorize,
11
+ )
12
+
13
+ __all__ = [
14
+ "get_logger",
15
+ "console",
16
+ "setup_logger",
17
+ "set_log_level",
18
+ "Colors",
19
+ "colorize",
20
+ ]
@@ -0,0 +1,526 @@
1
+ import logging
2
+ import os
3
+ import json
4
+ import contextvars
5
+ import re
6
+ from typing import Optional, List
7
+ from contextlib import contextmanager
8
+
9
+ """_dolphin_logger is the global logger for Dolphin SDK.
10
+
11
+ Developers can customize the logger configuration using the setup_logger() method.
12
+ Internal modules of Dolphin SDK can obtain the global logger of Dolphin SDK using the get_logger() method.
13
+ By default, the global logger of Dolphin SDK is created using the setup_default_logger() method.
14
+ """
15
+ _dolphin_logger = None
16
+
17
+ MaxLenLog = 4096
18
+
19
+ # Context variable storage, used to support independent log files during asyncio coroutine parallelization
20
+ # Fix: Changed from threading.local() to contextvars.ContextVar to support asynchronous coroutine isolation
21
+ _extra_log_file_var = contextvars.ContextVar("extra_log_file", default=None)
22
+
23
+ # Default logger name for Dolphin SDK
24
+ SDK_LOGGER_NAME = "dolphin_language"
25
+
26
+ # Pre-compiled ANSI escape sequences for removing console color codes
27
+ _ANSI_RE = re.compile(r"\x1b\[[0-9;]*m")
28
+
29
+
30
+ # ANSI color codes
31
+ class Colors:
32
+ # Basic Colors
33
+ RESET = "\033[0m"
34
+ BOLD = "\033[1m"
35
+ DIM = "\033[2m"
36
+ ITALIC = "\033[3m"
37
+ UNDERLINE = "\033[4m"
38
+
39
+ # Foreground color
40
+ BLACK = "\033[30m"
41
+ RED = "\033[31m"
42
+ GREEN = "\033[32m"
43
+ YELLOW = "\033[33m"
44
+ BLUE = "\033[34m"
45
+ MAGENTA = "\033[35m"
46
+ CYAN = "\033[36m"
47
+ WHITE = "\033[37m"
48
+
49
+ # Light color
50
+ BRIGHT_BLACK = "\033[90m"
51
+ BRIGHT_RED = "\033[91m"
52
+ BRIGHT_GREEN = "\033[92m"
53
+ BRIGHT_YELLOW = "\033[93m"
54
+ BRIGHT_BLUE = "\033[94m"
55
+ BRIGHT_MAGENTA = "\033[95m"
56
+ BRIGHT_CYAN = "\033[96m"
57
+ BRIGHT_WHITE = "\033[97m"
58
+
59
+ # Background color
60
+ BG_BLACK = "\033[40m"
61
+ BG_RED = "\033[41m"
62
+ BG_GREEN = "\033[42m"
63
+ BG_YELLOW = "\033[43m"
64
+ BG_BLUE = "\033[44m"
65
+ BG_MAGENTA = "\033[45m"
66
+ BG_CYAN = "\033[46m"
67
+ BG_WHITE = "\033[47m"
68
+
69
+
70
+ def colorize(text, color):
71
+ """Add color to text"""
72
+ return f"{color}{text}{Colors.RESET}"
73
+
74
+
75
+ def _strip_ansi(s: str) -> str:
76
+ """Remove ANSI color control codes for writing plain text logs"""
77
+ return _ANSI_RE.sub("", s)
78
+
79
+
80
+ @contextmanager
81
+ def extra_log_context(file_path: Optional[str]):
82
+ """Set additional log output context (based on contextvars, supports multi-coroutine isolation)
83
+
84
+ Usage example:
85
+ with extra_log_context('path/to/detail.log'):
86
+ ... # Calls to _write_to_extra_log/console within this context will also write to detail.log
87
+
88
+ Args:
89
+ file_path: Path to the additional log file, None means disable additional logging
90
+ """
91
+ # Ensure the parent directory exists (if a path is provided)
92
+ if file_path:
93
+ try:
94
+ os.makedirs(os.path.dirname(file_path), exist_ok=True)
95
+ except Exception:
96
+ # Directory creation failure does not affect the main process
97
+ pass
98
+
99
+ token = _extra_log_file_var.set(file_path)
100
+ try:
101
+ yield
102
+ finally:
103
+ # Restore previous context value
104
+ try:
105
+ _extra_log_file_var.reset(token)
106
+ except Exception:
107
+ # If the context has expired, silently ignore it.
108
+ pass
109
+
110
+
111
+ def write_to_log_file(text: str, *, ensure_newline: bool = True):
112
+ """Write a line to an additional log file (if additional log context exists).
113
+
114
+ - Automatically remove ANSI color codes and write plain text
115
+ - Open in append mode to avoid persistently holding file handles
116
+ - Thread-safe and coroutine-safe (write granularity is single write)
117
+
118
+ Args:
119
+ text: The text to write.
120
+ ensure_newline: Whether to ensure the text ends with a newline.
121
+ """
122
+ file_path = _extra_log_file_var.get()
123
+ if not file_path:
124
+ return
125
+ try:
126
+ s = str(text)
127
+ if ensure_newline and not s.endswith("\n"):
128
+ s += "\n"
129
+ s = _strip_ansi(s)
130
+ with open(file_path, "a", encoding="utf-8") as f:
131
+ f.write(s)
132
+ except Exception:
133
+ # Any log writing errors should not affect the main process
134
+ pass
135
+
136
+ # Alias for internal backward compatibility
137
+ _write_to_extra_log = write_to_log_file
138
+
139
+
140
+ def setup_logger(
141
+ level: int = logging.INFO,
142
+ format: str = "%(asctime)s - %(levelname)s - %(message)s",
143
+ datefmt: str = "%Y-%m-%d %H:%M:%S",
144
+ handlers: Optional[List[logging.Handler]] = None,
145
+ propagate: bool = False,
146
+ force: bool = False,
147
+ ) -> None:
148
+ """Set up the logger.
149
+
150
+ :param level: Log level, default is logging.INFO
151
+ :param format: Log format, default is logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
152
+ :param datefmt: Date format, default is "%Y-%m-%d %H:%M:%S"
153
+ :param handlers: List of log handlers, default is None
154
+ :param propagate: Whether to propagate logs, default is False
155
+ :param fore: Whether to clear existing handlers
156
+ """
157
+ _dolphin_logger = logging.getLogger(SDK_LOGGER_NAME)
158
+
159
+ # Avoid duplicate configuration
160
+ if not force and _dolphin_logger.handlers:
161
+ return
162
+
163
+ # Set log level
164
+ _dolphin_logger.setLevel(level)
165
+
166
+ # Set whether to pass to the parent logger
167
+ _dolphin_logger.propagate = propagate
168
+
169
+ formatter = logging.Formatter(format, datefmt)
170
+
171
+ if handlers:
172
+ for handler in handlers:
173
+ handler.setFormatter(formatter)
174
+ _dolphin_logger.addHandler(handler)
175
+ else:
176
+ console_handler = logging.StreamHandler()
177
+ console_handler.setFormatter(formatter)
178
+ _dolphin_logger.addHandler(console_handler)
179
+
180
+
181
+ def get_logger(sub_name: str = "") -> logging.Logger:
182
+ """Get the logger with the specified name.
183
+
184
+ :param name: The name of the logger
185
+ :return: The logger object
186
+ """
187
+ logger_name = f"{SDK_LOGGER_NAME}.{sub_name}" if sub_name else SDK_LOGGER_NAME
188
+ return logging.getLogger(logger_name)
189
+
190
+
191
+ # Set the default logger
192
+ def setup_default_logger(log_suffix: Optional[str] = None):
193
+ global _dolphin_logger
194
+
195
+ # If a logger already exists and no suffix is specified, return directly.
196
+ if _dolphin_logger is not None and log_suffix is None:
197
+ return _dolphin_logger
198
+
199
+ # If a suffix is specified, reconfiguration is required.
200
+ if log_suffix is not None and _dolphin_logger is not None:
201
+ # Clear existing processors
202
+ _dolphin_logger.handlers.clear()
203
+
204
+ _dolphin_logger = logging.getLogger(SDK_LOGGER_NAME)
205
+ _dolphin_logger.setLevel(logging.INFO)
206
+ _dolphin_logger.propagate = False
207
+
208
+ # Avoid duplicate processor addition
209
+ if not _dolphin_logger.handlers:
210
+ # Allow customizing log directory and file suffix via environment variables
211
+ # DOLPHIN_LOG_DIR: log directory (default "log")
212
+ # DOLPHIN_LOG_SUFFIX: File name suffix (default no suffix)
213
+ env_log_dir = os.getenv("DOLPHIN_LOG_DIR", "log")
214
+ env_suffix = os.getenv("DOLPHIN_LOG_SUFFIX")
215
+ # If log_suffix is not explicitly passed in, respect the environment variable
216
+ if log_suffix is None and env_suffix:
217
+ log_suffix = env_suffix
218
+
219
+ # Ensure the directory exists
220
+ try:
221
+ os.makedirs(env_log_dir, exist_ok=True)
222
+ except Exception:
223
+ # Failed to create directory, revert to default directory
224
+ env_log_dir = "log"
225
+ os.makedirs(env_log_dir, exist_ok=True)
226
+
227
+ # Build log filename
228
+ if log_suffix:
229
+ log_file = os.path.join(env_log_dir, f"dolphin_{log_suffix}.log")
230
+ else:
231
+ log_file = os.path.join(env_log_dir, "dolphin.log")
232
+
233
+ file_handler = logging.FileHandler(log_file)
234
+ file_handler.setFormatter(
235
+ logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
236
+ )
237
+ _dolphin_logger.addHandler(file_handler)
238
+
239
+
240
+ # Initialize the default logger
241
+ setup_default_logger()
242
+
243
+
244
+ def _format_json_compact(data, max_width=80):
245
+ """Format JSON data, keeping it compact within a reasonable width"""
246
+ if isinstance(data, dict):
247
+ json_str = json.dumps(data, ensure_ascii=False, separators=(",", ":"))
248
+ if len(json_str) <= max_width:
249
+ return json_str
250
+ else:
251
+ # If too long, try formatting
252
+ formatted = json.dumps(data, ensure_ascii=False, indent=2)
253
+ lines = formatted.split("\n")
254
+ if len(lines) <= 5: # If the number of lines is not large, return the formatted version
255
+ return formatted
256
+ else: # Otherwise, return the truncated compact version
257
+ if len(json_str) > max_width:
258
+ return json_str[: max_width - 3] + "..."
259
+ return json_str
260
+ return str(data)
261
+
262
+
263
+ def _stdout(value: str, info: bool = False, **kwargs):
264
+ if info or ("verbose" in kwargs and kwargs["verbose"]):
265
+ # Console Output
266
+ print(value, **{k: v for k, v in kwargs.items() if k in ["end", "flush"]})
267
+ # Synchronously write additional logs (if context is set)
268
+ end = kwargs.get("end", "\n")
269
+ if end:
270
+ _write_to_extra_log(
271
+ value + ("" if end is None else end), ensure_newline=False
272
+ )
273
+ else:
274
+ _write_to_extra_log(value, ensure_newline=False)
275
+
276
+
277
+ def console_skill_call(skill_name, params, max_length=200, verbose=None, skill=None):
278
+ """Display skill/tool call with modern styling.
279
+
280
+ This function uses the new console_ui module for enhanced visual display
281
+ inspired by Codex CLI and Claude Code's terminal interfaces.
282
+
283
+ Supports custom UI rendering via Skillkit.has_custom_ui() protocol.
284
+
285
+ Args:
286
+ skill_name: Name of the skill being called
287
+ params: Parameters passed to the skill
288
+ max_length: Maximum length for parameter display
289
+ verbose: Whether to display (None uses default, False suppresses)
290
+ skill: Optional SkillFunction object (for custom UI lookup)
291
+ """
292
+ if verbose is False:
293
+ return
294
+
295
+ # Check if skill has custom UI rendering
296
+ if skill and hasattr(skill, 'owner_skillkit') and skill.owner_skillkit:
297
+ skillkit = skill.owner_skillkit
298
+ if hasattr(skillkit, 'has_custom_ui') and skillkit.has_custom_ui(skill_name):
299
+ # Use custom rendering (start phase)
300
+ if hasattr(skillkit, 'render_skill_start'):
301
+ skillkit.render_skill_start(skill_name, params, verbose=verbose if verbose is not None else True)
302
+ return # Skip default rendering
303
+
304
+ try:
305
+ from dolphin.cli.ui.console import get_console_ui
306
+ ui = get_console_ui()
307
+ ui.skill_call_start(skill_name, params, max_length, verbose=True)
308
+ except ImportError:
309
+ # Fallback to legacy display if console_ui not available
310
+ header = f"{Colors.BOLD}{Colors.BRIGHT_CYAN}๐Ÿ”ง CALL SKILL{Colors.RESET} {Colors.BOLD}{Colors.BRIGHT_YELLOW}<{skill_name}>{Colors.RESET}"
311
+ separator = f"{Colors.DIM}{Colors.BRIGHT_BLACK}{'โ”€' * 60}{Colors.RESET}"
312
+ formatted_params = _format_json_compact(params, max_width=100)
313
+ if len(formatted_params) > max_length:
314
+ formatted_params = formatted_params[:max_length] + "..."
315
+ input_line = f"{Colors.BRIGHT_GREEN}>>> {Colors.RESET}{Colors.CYAN}{formatted_params}{Colors.RESET}"
316
+ print(separator)
317
+ print(header)
318
+ print(input_line)
319
+
320
+
321
+ def console_skill_response(skill_name, response, max_length=200, verbose=None, skill=None, params=None, duration_ms=0):
322
+ """Display skill/tool response with modern styling.
323
+
324
+ This function uses the new console_ui module for enhanced visual display
325
+ inspired by Codex CLI and Claude Code's terminal interfaces.
326
+
327
+ Supports custom UI rendering via Skillkit.has_custom_ui() protocol.
328
+
329
+ Args:
330
+ skill_name: Name of the skill that completed
331
+ response: Response from the skill
332
+ max_length: Maximum length for response display
333
+ verbose: Whether to display (None uses default, False suppresses)
334
+ skill: Optional SkillFunction object (for custom UI lookup)
335
+ params: Optional parameters that were passed to the skill
336
+ duration_ms: Execution duration in milliseconds
337
+ """
338
+ from dolphin.lib.skillkits.cognitive_skillkit import CognitiveSkillkit
339
+
340
+ if CognitiveSkillkit.is_cognitive_skill(skill_name):
341
+ return
342
+
343
+ if verbose is False:
344
+ return
345
+
346
+ # Check if skill has custom UI rendering
347
+ if skill and hasattr(skill, 'owner_skillkit') and skill.owner_skillkit:
348
+ skillkit = skill.owner_skillkit
349
+ if hasattr(skillkit, 'has_custom_ui') and skillkit.has_custom_ui(skill_name):
350
+ # Use custom rendering (end phase)
351
+ if hasattr(skillkit, 'render_skill_end'):
352
+ skillkit.render_skill_end(
353
+ skill_name,
354
+ params or {},
355
+ response,
356
+ success=True,
357
+ duration_ms=duration_ms,
358
+ verbose=verbose if verbose is not None else True
359
+ )
360
+ return # Skip default rendering
361
+
362
+ try:
363
+ from dolphin.cli.ui.console import get_console_ui
364
+ ui = get_console_ui()
365
+ ui.skill_call_end(skill_name, response, max_length, success=True, verbose=True)
366
+ except ImportError:
367
+ # Fallback to legacy display if console_ui not available
368
+ if isinstance(response, dict):
369
+ display_response = _format_json_compact(response, max_width=100)
370
+ elif not isinstance(response, str):
371
+ display_response = str(response)
372
+ else:
373
+ display_response = response
374
+
375
+ if len(display_response) > max_length:
376
+ display_response = display_response[:max_length] + "..."
377
+
378
+ output_line = f"{Colors.BRIGHT_BLUE}<<< {Colors.RESET}{Colors.BRIGHT_WHITE}{display_response}{Colors.RESET}"
379
+ separator = f"{Colors.DIM}{Colors.BRIGHT_BLACK}{'โ”€' * 60}{Colors.RESET}"
380
+ print(f"\n{output_line}")
381
+ print(f"{separator}\n")
382
+
383
+
384
+ def console_agent_skill_exit(skill_name: str, message: str = None, verbose=None):
385
+ """Display a delimiter indicating an agent-as-skill has exited.
386
+
387
+ This keeps terminal formatting concerns in the UI layer.
388
+ """
389
+ if verbose is False:
390
+ return
391
+
392
+ try:
393
+ from dolphin.cli.ui.console import get_console_ui
394
+
395
+ ui = get_console_ui()
396
+ ui.agent_skill_exit(skill_name, message=message, verbose=True)
397
+ except ImportError:
398
+ # Fallback: plain separator
399
+ label = message or f" AGENT EXITED: {skill_name}"
400
+ print("\n" + ("โ”€" * 10) + label + ("โ”€" * 10) + "\n")
401
+
402
+
403
+ def console_agent_skill_enter(skill_name: str, message: str = None, verbose=None):
404
+ """Display a delimiter indicating an agent-as-skill has started.
405
+
406
+ This keeps terminal formatting concerns in the UI layer.
407
+ """
408
+ if verbose is False:
409
+ return
410
+
411
+ try:
412
+ from dolphin.cli.ui.console import get_console_ui
413
+ ui = get_console_ui()
414
+ ui.agent_skill_enter(skill_name, message=message, verbose=True)
415
+ except ImportError:
416
+ label = message or f"๐Ÿš€ AGENT ACTIVATE: {skill_name}"
417
+ print(f"\n===== {label} =====\n")
418
+
419
+
420
+ def console_block_start(
421
+ block_name: str, output_var: str, content: Optional[str] = None, verbose=None
422
+ ):
423
+ """Display code block start with modern styling.
424
+
425
+ This function uses the new console_ui module for enhanced visual display
426
+ inspired by Codex CLI and Claude Code's terminal interfaces.
427
+
428
+ Args:
429
+ block_name: Type of block (explore, prompt, judge, assign, tool)
430
+ output_var: Variable name to store the output
431
+ content: Optional content preview
432
+ verbose: Whether to display (None uses default, False suppresses)
433
+ """
434
+ if verbose is False:
435
+ return
436
+
437
+ try:
438
+ from dolphin.cli.ui.console import get_console_ui
439
+ ui = get_console_ui()
440
+ ui.block_start(block_name, output_var, content, verbose=True)
441
+ except ImportError:
442
+ # Fallback to legacy display if console_ui not available
443
+ block_icons = {
444
+ "explore": "๐Ÿ”",
445
+ "prompt": "๐Ÿ’ฌ",
446
+ "judge": "โš–๏ธ",
447
+ "assign": "๐Ÿ“",
448
+ "tool": "๐Ÿ”ง",
449
+ }
450
+
451
+ block_colors = {
452
+ "explore": Colors.BRIGHT_MAGENTA,
453
+ "prompt": Colors.BRIGHT_GREEN,
454
+ "judge": Colors.BRIGHT_YELLOW,
455
+ "assign": Colors.BRIGHT_BLUE,
456
+ "tool": Colors.BRIGHT_CYAN,
457
+ }
458
+
459
+ icon = block_icons.get(block_name, "๐Ÿ“ฆ")
460
+ color = block_colors.get(block_name, Colors.BRIGHT_WHITE)
461
+
462
+ header = f"{color}{Colors.BOLD}{icon} {block_name.upper()}{Colors.RESET}"
463
+ var_name = f"{Colors.BRIGHT_CYAN}{output_var}{Colors.RESET}"
464
+ arrow = f"{Colors.DIM}โ†’{Colors.RESET}"
465
+
466
+ if content:
467
+ content = str(content)
468
+ if content.strip():
469
+ content_preview = (
470
+ content.strip()[:50] + "..."
471
+ if len(content.strip()) > 50
472
+ else content.strip()
473
+ )
474
+ content_part = f" {Colors.DIM}{content_preview}{Colors.RESET}"
475
+ else:
476
+ content_part = ""
477
+ else:
478
+ content_part = ""
479
+
480
+ output_line = f"{header} {var_name} {arrow}{content_part}"
481
+ print(output_line)
482
+
483
+
484
+ def set_log_level(level: int = logging.INFO, force: bool = False):
485
+ """Dynamically set the log level of the global logger
486
+
487
+ Args:
488
+ level: Log level, such as logging.DEBUG, logging.INFO, logging.WARNING, etc.
489
+ force: Whether to forcibly set, even if the logger already has handlers
490
+ """
491
+ global _dolphin_logger
492
+
493
+ if _dolphin_logger is None:
494
+ # If the logger has not been initialized yet, initialize it first.
495
+ setup_default_logger()
496
+ _dolphin_logger.setLevel(level)
497
+
498
+ # If forcibly set, clear existing handlers and reset them.
499
+ if force:
500
+ _dolphin_logger.handlers.clear()
501
+ setup_default_logger()
502
+ _dolphin_logger.setLevel(level)
503
+
504
+
505
+ def console(info, verbose=None, **kwargs):
506
+ """Console output function, supports verbose parameter control
507
+
508
+ Args:
509
+ info: Information to be output
510
+ verbose: Whether to display detailed information (if None, determine based on the logic in _stdout)
511
+ **kwargs: Other parameters, such as end, flush, etc.
512
+ """
513
+ if verbose is not None:
514
+ # If the verbose parameter is explicitly specified, use that value.
515
+ if "end" in kwargs:
516
+ _stdout(str(info), info=verbose, flush=True, end=kwargs["end"])
517
+ else:
518
+ _stdout(str(info), info=verbose, flush=True)
519
+ else:
520
+ # Keep the original logic and get verbose from kwargs
521
+ if "end" in kwargs:
522
+ _stdout(str(info), info=True, flush=True, end=kwargs["end"])
523
+ else:
524
+ _stdout(str(info), info=True, flush=True)
525
+
526
+
@@ -0,0 +1,8 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Message ๆจกๅ— - ๆถˆๆฏๅŽ‹็ผฉ"""
3
+
4
+ from dolphin.core.message.compressor import MessageCompressor
5
+
6
+ __all__ = [
7
+ "MessageCompressor",
8
+ ]