superqode 0.1.5__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.
- superqode/__init__.py +33 -0
- superqode/acp/__init__.py +23 -0
- superqode/acp/client.py +913 -0
- superqode/acp/permission_screen.py +457 -0
- superqode/acp/types.py +480 -0
- superqode/acp_discovery.py +856 -0
- superqode/agent/__init__.py +22 -0
- superqode/agent/edit_strategies.py +334 -0
- superqode/agent/loop.py +892 -0
- superqode/agent/qe_report_templates.py +39 -0
- superqode/agent/system_prompts.py +353 -0
- superqode/agent_output.py +721 -0
- superqode/agent_stream.py +953 -0
- superqode/agents/__init__.py +59 -0
- superqode/agents/acp_registry.py +305 -0
- superqode/agents/client.py +249 -0
- superqode/agents/data/augmentcode.com.toml +51 -0
- superqode/agents/data/cagent.dev.toml +51 -0
- superqode/agents/data/claude.com.toml +60 -0
- superqode/agents/data/codeassistant.dev.toml +51 -0
- superqode/agents/data/codex.openai.com.toml +57 -0
- superqode/agents/data/fastagent.ai.toml +66 -0
- superqode/agents/data/geminicli.com.toml +77 -0
- superqode/agents/data/goose.block.xyz.toml +54 -0
- superqode/agents/data/junie.jetbrains.com.toml +56 -0
- superqode/agents/data/kimi.moonshot.cn.toml +57 -0
- superqode/agents/data/llmlingagent.dev.toml +51 -0
- superqode/agents/data/molt.bot.toml +49 -0
- superqode/agents/data/opencode.ai.toml +60 -0
- superqode/agents/data/stakpak.dev.toml +51 -0
- superqode/agents/data/vtcode.dev.toml +51 -0
- superqode/agents/discovery.py +266 -0
- superqode/agents/messaging.py +160 -0
- superqode/agents/persona.py +166 -0
- superqode/agents/registry.py +421 -0
- superqode/agents/schema.py +72 -0
- superqode/agents/unified.py +367 -0
- superqode/app/__init__.py +111 -0
- superqode/app/constants.py +314 -0
- superqode/app/css.py +366 -0
- superqode/app/models.py +118 -0
- superqode/app/suggester.py +125 -0
- superqode/app/widgets.py +1591 -0
- superqode/app_enhanced.py +399 -0
- superqode/app_main.py +17187 -0
- superqode/approval.py +312 -0
- superqode/atomic.py +296 -0
- superqode/commands/__init__.py +1 -0
- superqode/commands/acp.py +965 -0
- superqode/commands/agents.py +180 -0
- superqode/commands/auth.py +278 -0
- superqode/commands/config.py +374 -0
- superqode/commands/init.py +826 -0
- superqode/commands/providers.py +819 -0
- superqode/commands/qe.py +1145 -0
- superqode/commands/roles.py +380 -0
- superqode/commands/serve.py +172 -0
- superqode/commands/suggestions.py +127 -0
- superqode/commands/superqe.py +460 -0
- superqode/config/__init__.py +51 -0
- superqode/config/loader.py +812 -0
- superqode/config/schema.py +498 -0
- superqode/core/__init__.py +111 -0
- superqode/core/roles.py +281 -0
- superqode/danger.py +386 -0
- superqode/data/superqode-template.yaml +1522 -0
- superqode/design_system.py +1080 -0
- superqode/dialogs/__init__.py +6 -0
- superqode/dialogs/base.py +39 -0
- superqode/dialogs/model.py +130 -0
- superqode/dialogs/provider.py +870 -0
- superqode/diff_view.py +919 -0
- superqode/enterprise.py +21 -0
- superqode/evaluation/__init__.py +25 -0
- superqode/evaluation/adapters.py +93 -0
- superqode/evaluation/behaviors.py +89 -0
- superqode/evaluation/engine.py +209 -0
- superqode/evaluation/scenarios.py +96 -0
- superqode/execution/__init__.py +36 -0
- superqode/execution/linter.py +538 -0
- superqode/execution/modes.py +347 -0
- superqode/execution/resolver.py +283 -0
- superqode/execution/runner.py +642 -0
- superqode/file_explorer.py +811 -0
- superqode/file_viewer.py +471 -0
- superqode/flash.py +183 -0
- superqode/guidance/__init__.py +58 -0
- superqode/guidance/config.py +203 -0
- superqode/guidance/prompts.py +71 -0
- superqode/harness/__init__.py +54 -0
- superqode/harness/accelerator.py +291 -0
- superqode/harness/config.py +319 -0
- superqode/harness/validator.py +147 -0
- superqode/history.py +279 -0
- superqode/integrations/superopt_runner.py +124 -0
- superqode/logging/__init__.py +49 -0
- superqode/logging/adapters.py +219 -0
- superqode/logging/formatter.py +923 -0
- superqode/logging/integration.py +341 -0
- superqode/logging/sinks.py +170 -0
- superqode/logging/unified_log.py +417 -0
- superqode/lsp/__init__.py +26 -0
- superqode/lsp/client.py +544 -0
- superqode/main.py +1069 -0
- superqode/mcp/__init__.py +89 -0
- superqode/mcp/auth_storage.py +380 -0
- superqode/mcp/client.py +1236 -0
- superqode/mcp/config.py +319 -0
- superqode/mcp/integration.py +337 -0
- superqode/mcp/oauth.py +436 -0
- superqode/mcp/oauth_callback.py +385 -0
- superqode/mcp/types.py +290 -0
- superqode/memory/__init__.py +31 -0
- superqode/memory/feedback.py +342 -0
- superqode/memory/store.py +522 -0
- superqode/notifications.py +369 -0
- superqode/optimization/__init__.py +5 -0
- superqode/optimization/config.py +33 -0
- superqode/permissions/__init__.py +25 -0
- superqode/permissions/rules.py +488 -0
- superqode/plan.py +323 -0
- superqode/providers/__init__.py +33 -0
- superqode/providers/gateway/__init__.py +165 -0
- superqode/providers/gateway/base.py +228 -0
- superqode/providers/gateway/litellm_gateway.py +1170 -0
- superqode/providers/gateway/openresponses_gateway.py +436 -0
- superqode/providers/health.py +297 -0
- superqode/providers/huggingface/__init__.py +74 -0
- superqode/providers/huggingface/downloader.py +472 -0
- superqode/providers/huggingface/endpoints.py +442 -0
- superqode/providers/huggingface/hub.py +531 -0
- superqode/providers/huggingface/inference.py +394 -0
- superqode/providers/huggingface/transformers_runner.py +516 -0
- superqode/providers/local/__init__.py +100 -0
- superqode/providers/local/base.py +438 -0
- superqode/providers/local/discovery.py +418 -0
- superqode/providers/local/lmstudio.py +256 -0
- superqode/providers/local/mlx.py +457 -0
- superqode/providers/local/ollama.py +486 -0
- superqode/providers/local/sglang.py +268 -0
- superqode/providers/local/tgi.py +260 -0
- superqode/providers/local/tool_support.py +477 -0
- superqode/providers/local/vllm.py +258 -0
- superqode/providers/manager.py +1338 -0
- superqode/providers/models.py +1016 -0
- superqode/providers/models_dev.py +578 -0
- superqode/providers/openresponses/__init__.py +87 -0
- superqode/providers/openresponses/converters/__init__.py +17 -0
- superqode/providers/openresponses/converters/messages.py +343 -0
- superqode/providers/openresponses/converters/tools.py +268 -0
- superqode/providers/openresponses/schema/__init__.py +56 -0
- superqode/providers/openresponses/schema/models.py +585 -0
- superqode/providers/openresponses/streaming/__init__.py +5 -0
- superqode/providers/openresponses/streaming/parser.py +338 -0
- superqode/providers/openresponses/tools/__init__.py +21 -0
- superqode/providers/openresponses/tools/apply_patch.py +352 -0
- superqode/providers/openresponses/tools/code_interpreter.py +290 -0
- superqode/providers/openresponses/tools/file_search.py +333 -0
- superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
- superqode/providers/registry.py +716 -0
- superqode/providers/usage.py +332 -0
- superqode/pure_mode.py +384 -0
- superqode/qr/__init__.py +23 -0
- superqode/qr/dashboard.py +781 -0
- superqode/qr/generator.py +1018 -0
- superqode/qr/templates.py +135 -0
- superqode/safety/__init__.py +41 -0
- superqode/safety/sandbox.py +413 -0
- superqode/safety/warnings.py +256 -0
- superqode/server/__init__.py +33 -0
- superqode/server/lsp_server.py +775 -0
- superqode/server/web.py +250 -0
- superqode/session/__init__.py +25 -0
- superqode/session/persistence.py +580 -0
- superqode/session/sharing.py +477 -0
- superqode/session.py +475 -0
- superqode/sidebar.py +2991 -0
- superqode/stream_view.py +648 -0
- superqode/styles/__init__.py +3 -0
- superqode/superqe/__init__.py +184 -0
- superqode/superqe/acp_runner.py +1064 -0
- superqode/superqe/constitution/__init__.py +62 -0
- superqode/superqe/constitution/evaluator.py +308 -0
- superqode/superqe/constitution/loader.py +432 -0
- superqode/superqe/constitution/schema.py +250 -0
- superqode/superqe/events.py +591 -0
- superqode/superqe/frameworks/__init__.py +65 -0
- superqode/superqe/frameworks/base.py +234 -0
- superqode/superqe/frameworks/e2e.py +263 -0
- superqode/superqe/frameworks/executor.py +237 -0
- superqode/superqe/frameworks/javascript.py +409 -0
- superqode/superqe/frameworks/python.py +373 -0
- superqode/superqe/frameworks/registry.py +92 -0
- superqode/superqe/mcp_tools/__init__.py +47 -0
- superqode/superqe/mcp_tools/core_tools.py +418 -0
- superqode/superqe/mcp_tools/registry.py +230 -0
- superqode/superqe/mcp_tools/testing_tools.py +167 -0
- superqode/superqe/noise.py +89 -0
- superqode/superqe/orchestrator.py +778 -0
- superqode/superqe/roles.py +609 -0
- superqode/superqe/session.py +713 -0
- superqode/superqe/skills/__init__.py +57 -0
- superqode/superqe/skills/base.py +106 -0
- superqode/superqe/skills/core_skills.py +899 -0
- superqode/superqe/skills/registry.py +90 -0
- superqode/superqe/verifier.py +101 -0
- superqode/superqe_cli.py +76 -0
- superqode/tool_call.py +358 -0
- superqode/tools/__init__.py +93 -0
- superqode/tools/agent_tools.py +496 -0
- superqode/tools/base.py +324 -0
- superqode/tools/batch_tool.py +133 -0
- superqode/tools/diagnostics.py +311 -0
- superqode/tools/edit_tools.py +653 -0
- superqode/tools/enhanced_base.py +515 -0
- superqode/tools/file_tools.py +269 -0
- superqode/tools/file_tracking.py +45 -0
- superqode/tools/lsp_tools.py +610 -0
- superqode/tools/network_tools.py +350 -0
- superqode/tools/permissions.py +400 -0
- superqode/tools/question_tool.py +324 -0
- superqode/tools/search_tools.py +598 -0
- superqode/tools/shell_tools.py +259 -0
- superqode/tools/todo_tools.py +121 -0
- superqode/tools/validation.py +80 -0
- superqode/tools/web_tools.py +639 -0
- superqode/tui.py +1152 -0
- superqode/tui_integration.py +875 -0
- superqode/tui_widgets/__init__.py +27 -0
- superqode/tui_widgets/widgets/__init__.py +18 -0
- superqode/tui_widgets/widgets/progress.py +185 -0
- superqode/tui_widgets/widgets/tool_display.py +188 -0
- superqode/undo_manager.py +574 -0
- superqode/utils/__init__.py +5 -0
- superqode/utils/error_handling.py +323 -0
- superqode/utils/fuzzy.py +257 -0
- superqode/widgets/__init__.py +477 -0
- superqode/widgets/agent_collab.py +390 -0
- superqode/widgets/agent_store.py +936 -0
- superqode/widgets/agent_switcher.py +395 -0
- superqode/widgets/animation_manager.py +284 -0
- superqode/widgets/code_context.py +356 -0
- superqode/widgets/command_palette.py +412 -0
- superqode/widgets/connection_status.py +537 -0
- superqode/widgets/conversation_history.py +470 -0
- superqode/widgets/diff_indicator.py +155 -0
- superqode/widgets/enhanced_status_bar.py +385 -0
- superqode/widgets/enhanced_toast.py +476 -0
- superqode/widgets/file_browser.py +809 -0
- superqode/widgets/file_reference.py +585 -0
- superqode/widgets/issue_timeline.py +340 -0
- superqode/widgets/leader_key.py +264 -0
- superqode/widgets/mode_switcher.py +445 -0
- superqode/widgets/model_picker.py +234 -0
- superqode/widgets/permission_preview.py +1205 -0
- superqode/widgets/prompt.py +358 -0
- superqode/widgets/provider_connect.py +725 -0
- superqode/widgets/pty_shell.py +587 -0
- superqode/widgets/qe_dashboard.py +321 -0
- superqode/widgets/resizable_sidebar.py +377 -0
- superqode/widgets/response_changes.py +218 -0
- superqode/widgets/response_display.py +528 -0
- superqode/widgets/rich_tool_display.py +613 -0
- superqode/widgets/sidebar_panels.py +1180 -0
- superqode/widgets/slash_complete.py +356 -0
- superqode/widgets/split_view.py +612 -0
- superqode/widgets/status_bar.py +273 -0
- superqode/widgets/superqode_display.py +786 -0
- superqode/widgets/thinking_display.py +815 -0
- superqode/widgets/throbber.py +87 -0
- superqode/widgets/toast.py +206 -0
- superqode/widgets/unified_output.py +1073 -0
- superqode/workspace/__init__.py +75 -0
- superqode/workspace/artifacts.py +472 -0
- superqode/workspace/coordinator.py +353 -0
- superqode/workspace/diff_tracker.py +429 -0
- superqode/workspace/git_guard.py +373 -0
- superqode/workspace/git_snapshot.py +526 -0
- superqode/workspace/manager.py +750 -0
- superqode/workspace/snapshot.py +357 -0
- superqode/workspace/watcher.py +535 -0
- superqode/workspace/worktree.py +440 -0
- superqode-0.1.5.dist-info/METADATA +204 -0
- superqode-0.1.5.dist-info/RECORD +288 -0
- superqode-0.1.5.dist-info/WHEEL +5 -0
- superqode-0.1.5.dist-info/entry_points.txt +3 -0
- superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
- superqode-0.1.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unified Logging Core for SuperQode.
|
|
3
|
+
|
|
4
|
+
Provides structured log entries and a unified logger that works consistently
|
|
5
|
+
across all provider modes (ACP, BYOK, Local/Ollama).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from enum import Enum
|
|
12
|
+
from time import monotonic
|
|
13
|
+
from typing import Any, Callable, Literal, Optional, Protocol
|
|
14
|
+
import uuid
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LogVerbosity(str, Enum):
|
|
18
|
+
"""Log verbosity levels."""
|
|
19
|
+
|
|
20
|
+
MINIMAL = "minimal" # Just status, no content
|
|
21
|
+
NORMAL = "normal" # Summarized content
|
|
22
|
+
VERBOSE = "verbose" # Full content (with truncation limits)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
LogKind = Literal[
|
|
26
|
+
"user",
|
|
27
|
+
"assistant",
|
|
28
|
+
"thinking",
|
|
29
|
+
"tool_call",
|
|
30
|
+
"tool_update",
|
|
31
|
+
"tool_result",
|
|
32
|
+
"info",
|
|
33
|
+
"warning",
|
|
34
|
+
"error",
|
|
35
|
+
"system",
|
|
36
|
+
"response_delta",
|
|
37
|
+
"response_final",
|
|
38
|
+
"code_block",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
LogSource = Literal["acp", "byok", "local", "system"]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class LogConfig:
|
|
46
|
+
"""Configuration for log display behavior."""
|
|
47
|
+
|
|
48
|
+
verbosity: LogVerbosity = LogVerbosity.NORMAL
|
|
49
|
+
show_thinking: bool = True
|
|
50
|
+
show_tool_args: bool = True
|
|
51
|
+
show_tool_result: bool = True
|
|
52
|
+
max_tool_output_chars: int = 2000
|
|
53
|
+
max_thinking_chars: int = 500
|
|
54
|
+
syntax_highlight: bool = True
|
|
55
|
+
code_theme: str = "github-dark"
|
|
56
|
+
|
|
57
|
+
@classmethod
|
|
58
|
+
def minimal(cls) -> LogConfig:
|
|
59
|
+
"""Create minimal verbosity config."""
|
|
60
|
+
return cls(
|
|
61
|
+
verbosity=LogVerbosity.MINIMAL,
|
|
62
|
+
show_thinking=False,
|
|
63
|
+
show_tool_args=False,
|
|
64
|
+
show_tool_result=False,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def normal(cls) -> LogConfig:
|
|
69
|
+
"""Create normal verbosity config."""
|
|
70
|
+
return cls(
|
|
71
|
+
verbosity=LogVerbosity.NORMAL,
|
|
72
|
+
show_thinking=True,
|
|
73
|
+
show_tool_args=True,
|
|
74
|
+
show_tool_result=True,
|
|
75
|
+
max_tool_output_chars=200,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def verbose(cls) -> LogConfig:
|
|
80
|
+
"""Create verbose config."""
|
|
81
|
+
return cls(
|
|
82
|
+
verbosity=LogVerbosity.VERBOSE,
|
|
83
|
+
show_thinking=True,
|
|
84
|
+
show_tool_args=True,
|
|
85
|
+
show_tool_result=True,
|
|
86
|
+
max_tool_output_chars=2000,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
def for_source(cls, source: LogSource) -> LogConfig:
|
|
91
|
+
"""Get recommended config for a source type."""
|
|
92
|
+
if source == "local":
|
|
93
|
+
# Local models can be verbose, default to less thinking display
|
|
94
|
+
return cls(
|
|
95
|
+
verbosity=LogVerbosity.NORMAL,
|
|
96
|
+
show_thinking=False, # Toggle with Ctrl+T
|
|
97
|
+
show_tool_args=True,
|
|
98
|
+
show_tool_result=True,
|
|
99
|
+
max_tool_output_chars=500,
|
|
100
|
+
)
|
|
101
|
+
elif source == "acp":
|
|
102
|
+
return cls(
|
|
103
|
+
verbosity=LogVerbosity.NORMAL,
|
|
104
|
+
show_thinking=True,
|
|
105
|
+
show_tool_args=True,
|
|
106
|
+
show_tool_result=True,
|
|
107
|
+
)
|
|
108
|
+
else: # byok
|
|
109
|
+
return cls.normal()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@dataclass
|
|
113
|
+
class LogEntry:
|
|
114
|
+
"""
|
|
115
|
+
A structured log entry.
|
|
116
|
+
|
|
117
|
+
This is the single source of truth for all log events across providers.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
kind: LogKind
|
|
121
|
+
source: LogSource
|
|
122
|
+
text: str = ""
|
|
123
|
+
data: dict[str, Any] = field(default_factory=dict)
|
|
124
|
+
agent: str = "Assistant"
|
|
125
|
+
ts: float = field(default_factory=monotonic)
|
|
126
|
+
span_id: Optional[str] = None
|
|
127
|
+
level: int = 0 # 0=always, 1=normal+verbose, 2=verbose only
|
|
128
|
+
|
|
129
|
+
def __post_init__(self):
|
|
130
|
+
if self.span_id is None and self.kind in ("tool_call", "tool_update", "tool_result"):
|
|
131
|
+
self.span_id = str(uuid.uuid4())[:8]
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def tool_name(self) -> str:
|
|
135
|
+
"""Get tool name from data."""
|
|
136
|
+
return self.data.get("tool_name", "")
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def tool_args(self) -> dict:
|
|
140
|
+
"""Get tool arguments from data."""
|
|
141
|
+
return self.data.get("args", {})
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def tool_result_text(self) -> str:
|
|
145
|
+
"""Get tool result text from data."""
|
|
146
|
+
return str(self.data.get("result", ""))
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def is_success(self) -> bool:
|
|
150
|
+
"""Check if tool result was successful."""
|
|
151
|
+
return self.data.get("ok", True)
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def file_path(self) -> str:
|
|
155
|
+
"""Extract file path from tool args."""
|
|
156
|
+
args = self.tool_args
|
|
157
|
+
return args.get("path", args.get("file_path", args.get("filePath", "")))
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def command(self) -> str:
|
|
161
|
+
"""Extract command from tool args."""
|
|
162
|
+
return self.tool_args.get("command", "")
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def thinking(
|
|
166
|
+
cls,
|
|
167
|
+
text: str,
|
|
168
|
+
source: LogSource = "byok",
|
|
169
|
+
category: str = "general",
|
|
170
|
+
) -> LogEntry:
|
|
171
|
+
"""Create a thinking log entry."""
|
|
172
|
+
return cls(
|
|
173
|
+
kind="thinking",
|
|
174
|
+
source=source,
|
|
175
|
+
text=text,
|
|
176
|
+
data={"category": category},
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
@classmethod
|
|
180
|
+
def tool_call(
|
|
181
|
+
cls,
|
|
182
|
+
name: str,
|
|
183
|
+
args: dict,
|
|
184
|
+
source: LogSource = "byok",
|
|
185
|
+
span_id: Optional[str] = None,
|
|
186
|
+
) -> LogEntry:
|
|
187
|
+
"""Create a tool call log entry."""
|
|
188
|
+
return cls(
|
|
189
|
+
kind="tool_call",
|
|
190
|
+
source=source,
|
|
191
|
+
text=f"Calling {name}",
|
|
192
|
+
data={"tool_name": name, "args": args},
|
|
193
|
+
span_id=span_id or str(uuid.uuid4())[:8],
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
@classmethod
|
|
197
|
+
def tool_result(
|
|
198
|
+
cls,
|
|
199
|
+
name: str,
|
|
200
|
+
result: Any,
|
|
201
|
+
success: bool = True,
|
|
202
|
+
source: LogSource = "byok",
|
|
203
|
+
span_id: Optional[str] = None,
|
|
204
|
+
) -> LogEntry:
|
|
205
|
+
"""Create a tool result log entry."""
|
|
206
|
+
result_text = str(result) if result else ""
|
|
207
|
+
return cls(
|
|
208
|
+
kind="tool_result",
|
|
209
|
+
source=source,
|
|
210
|
+
text=f"{name} {'completed' if success else 'failed'}",
|
|
211
|
+
data={"tool_name": name, "result": result_text, "ok": success},
|
|
212
|
+
span_id=span_id,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
@classmethod
|
|
216
|
+
def response(
|
|
217
|
+
cls,
|
|
218
|
+
text: str,
|
|
219
|
+
source: LogSource = "byok",
|
|
220
|
+
agent: str = "Assistant",
|
|
221
|
+
is_final: bool = False,
|
|
222
|
+
) -> LogEntry:
|
|
223
|
+
"""Create a response log entry."""
|
|
224
|
+
return cls(
|
|
225
|
+
kind="response_final" if is_final else "response_delta",
|
|
226
|
+
source=source,
|
|
227
|
+
text=text,
|
|
228
|
+
agent=agent,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
@classmethod
|
|
232
|
+
def code_block(
|
|
233
|
+
cls,
|
|
234
|
+
code: str,
|
|
235
|
+
language: str = "",
|
|
236
|
+
source: LogSource = "local",
|
|
237
|
+
) -> LogEntry:
|
|
238
|
+
"""Create a code block log entry for proper syntax highlighting."""
|
|
239
|
+
return cls(
|
|
240
|
+
kind="code_block",
|
|
241
|
+
source=source,
|
|
242
|
+
text=code,
|
|
243
|
+
data={"language": language},
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
@classmethod
|
|
247
|
+
def info(cls, text: str, source: LogSource = "system") -> LogEntry:
|
|
248
|
+
"""Create an info log entry."""
|
|
249
|
+
return cls(kind="info", source=source, text=text)
|
|
250
|
+
|
|
251
|
+
@classmethod
|
|
252
|
+
def error(cls, text: str, source: LogSource = "system") -> LogEntry:
|
|
253
|
+
"""Create an error log entry."""
|
|
254
|
+
return cls(kind="error", source=source, text=text)
|
|
255
|
+
|
|
256
|
+
@classmethod
|
|
257
|
+
def warning(cls, text: str, source: LogSource = "system") -> LogEntry:
|
|
258
|
+
"""Create a warning log entry."""
|
|
259
|
+
return cls(kind="warning", source=source, text=text)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class LogSink(Protocol):
|
|
263
|
+
"""Protocol for log output destinations."""
|
|
264
|
+
|
|
265
|
+
def emit(self, entry: LogEntry, config: LogConfig) -> None:
|
|
266
|
+
"""Emit a log entry."""
|
|
267
|
+
...
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class UnifiedLogger:
|
|
271
|
+
"""
|
|
272
|
+
Unified logger that routes events to sinks based on configuration.
|
|
273
|
+
|
|
274
|
+
This is the central routing point for all log events across providers.
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
def __init__(
|
|
278
|
+
self,
|
|
279
|
+
config: Optional[LogConfig] = None,
|
|
280
|
+
sink: Optional[LogSink] = None,
|
|
281
|
+
):
|
|
282
|
+
self.config = config or LogConfig.normal()
|
|
283
|
+
self._sinks: list[LogSink] = []
|
|
284
|
+
if sink:
|
|
285
|
+
self._sinks.append(sink)
|
|
286
|
+
self._buffer: list[LogEntry] = []
|
|
287
|
+
self._response_buffer: str = ""
|
|
288
|
+
self._on_entry: Optional[Callable[[LogEntry], None]] = None
|
|
289
|
+
|
|
290
|
+
def add_sink(self, sink: LogSink) -> None:
|
|
291
|
+
"""Add a log sink."""
|
|
292
|
+
self._sinks.append(sink)
|
|
293
|
+
|
|
294
|
+
def remove_sink(self, sink: LogSink) -> None:
|
|
295
|
+
"""Remove a log sink."""
|
|
296
|
+
if sink in self._sinks:
|
|
297
|
+
self._sinks.remove(sink)
|
|
298
|
+
|
|
299
|
+
def set_verbosity(self, verbosity: LogVerbosity) -> None:
|
|
300
|
+
"""Change verbosity level."""
|
|
301
|
+
self.config.verbosity = verbosity
|
|
302
|
+
if verbosity == LogVerbosity.MINIMAL:
|
|
303
|
+
self.config.show_thinking = False
|
|
304
|
+
self.config.show_tool_args = False
|
|
305
|
+
self.config.show_tool_result = False
|
|
306
|
+
elif verbosity == LogVerbosity.NORMAL:
|
|
307
|
+
self.config.show_thinking = True
|
|
308
|
+
self.config.show_tool_args = True
|
|
309
|
+
self.config.show_tool_result = True
|
|
310
|
+
self.config.max_tool_output_chars = 200
|
|
311
|
+
else: # verbose
|
|
312
|
+
self.config.show_thinking = True
|
|
313
|
+
self.config.show_tool_args = True
|
|
314
|
+
self.config.show_tool_result = True
|
|
315
|
+
self.config.max_tool_output_chars = 2000
|
|
316
|
+
|
|
317
|
+
def toggle_thinking(self) -> bool:
|
|
318
|
+
"""Toggle thinking display. Returns new state."""
|
|
319
|
+
self.config.show_thinking = not self.config.show_thinking
|
|
320
|
+
return self.config.show_thinking
|
|
321
|
+
|
|
322
|
+
def _should_emit(self, entry: LogEntry) -> bool:
|
|
323
|
+
"""Check if entry should be emitted based on config."""
|
|
324
|
+
# Check verbosity level
|
|
325
|
+
if entry.level == 2 and self.config.verbosity != LogVerbosity.VERBOSE:
|
|
326
|
+
return False
|
|
327
|
+
if entry.level == 1 and self.config.verbosity == LogVerbosity.MINIMAL:
|
|
328
|
+
return False
|
|
329
|
+
|
|
330
|
+
# Check thinking filter
|
|
331
|
+
if entry.kind == "thinking" and not self.config.show_thinking:
|
|
332
|
+
return False
|
|
333
|
+
|
|
334
|
+
return True
|
|
335
|
+
|
|
336
|
+
def log(self, entry: LogEntry) -> None:
|
|
337
|
+
"""Log an entry to all sinks."""
|
|
338
|
+
self._buffer.append(entry)
|
|
339
|
+
|
|
340
|
+
if self._on_entry:
|
|
341
|
+
self._on_entry(entry)
|
|
342
|
+
|
|
343
|
+
if not self._should_emit(entry):
|
|
344
|
+
return
|
|
345
|
+
|
|
346
|
+
for sink in self._sinks:
|
|
347
|
+
try:
|
|
348
|
+
sink.emit(entry, self.config)
|
|
349
|
+
except Exception:
|
|
350
|
+
pass # Don't let sink errors crash logging
|
|
351
|
+
|
|
352
|
+
def thinking(self, text: str, source: LogSource = "byok", category: str = "general") -> None:
|
|
353
|
+
"""Log a thinking entry."""
|
|
354
|
+
self.log(LogEntry.thinking(text, source, category))
|
|
355
|
+
|
|
356
|
+
def tool_call(
|
|
357
|
+
self,
|
|
358
|
+
name: str,
|
|
359
|
+
args: dict,
|
|
360
|
+
source: LogSource = "byok",
|
|
361
|
+
span_id: Optional[str] = None,
|
|
362
|
+
) -> str:
|
|
363
|
+
"""Log a tool call. Returns span_id for correlation."""
|
|
364
|
+
entry = LogEntry.tool_call(name, args, source, span_id)
|
|
365
|
+
self.log(entry)
|
|
366
|
+
return entry.span_id or ""
|
|
367
|
+
|
|
368
|
+
def tool_result(
|
|
369
|
+
self,
|
|
370
|
+
name: str,
|
|
371
|
+
result: Any,
|
|
372
|
+
success: bool = True,
|
|
373
|
+
source: LogSource = "byok",
|
|
374
|
+
span_id: Optional[str] = None,
|
|
375
|
+
) -> None:
|
|
376
|
+
"""Log a tool result."""
|
|
377
|
+
self.log(LogEntry.tool_result(name, result, success, source, span_id))
|
|
378
|
+
|
|
379
|
+
def response_chunk(
|
|
380
|
+
self, text: str, source: LogSource = "byok", agent: str = "Assistant"
|
|
381
|
+
) -> None:
|
|
382
|
+
"""Log a response chunk (streaming)."""
|
|
383
|
+
self._response_buffer += text
|
|
384
|
+
self.log(LogEntry.response(text, source, agent, is_final=False))
|
|
385
|
+
|
|
386
|
+
def response_complete(self, source: LogSource = "byok", agent: str = "Assistant") -> str:
|
|
387
|
+
"""Complete response streaming. Returns full response."""
|
|
388
|
+
full_response = self._response_buffer
|
|
389
|
+
if full_response:
|
|
390
|
+
self.log(LogEntry.response(full_response, source, agent, is_final=True))
|
|
391
|
+
self._response_buffer = ""
|
|
392
|
+
return full_response
|
|
393
|
+
|
|
394
|
+
def code_block(self, code: str, language: str = "", source: LogSource = "local") -> None:
|
|
395
|
+
"""Log a code block with syntax highlighting."""
|
|
396
|
+
self.log(LogEntry.code_block(code, language, source))
|
|
397
|
+
|
|
398
|
+
def info(self, text: str) -> None:
|
|
399
|
+
"""Log info message."""
|
|
400
|
+
self.log(LogEntry.info(text))
|
|
401
|
+
|
|
402
|
+
def error(self, text: str) -> None:
|
|
403
|
+
"""Log error message."""
|
|
404
|
+
self.log(LogEntry.error(text))
|
|
405
|
+
|
|
406
|
+
def warning(self, text: str) -> None:
|
|
407
|
+
"""Log warning message."""
|
|
408
|
+
self.log(LogEntry.warning(text))
|
|
409
|
+
|
|
410
|
+
def get_history(self) -> list[LogEntry]:
|
|
411
|
+
"""Get log history."""
|
|
412
|
+
return self._buffer.copy()
|
|
413
|
+
|
|
414
|
+
def clear(self) -> None:
|
|
415
|
+
"""Clear log history."""
|
|
416
|
+
self._buffer.clear()
|
|
417
|
+
self._response_buffer = ""
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SuperQode LSP Integration.
|
|
3
|
+
|
|
4
|
+
Language Server Protocol support for real-time diagnostics,
|
|
5
|
+
code intelligence, and IDE-like features.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .client import (
|
|
9
|
+
LSPClient,
|
|
10
|
+
LSPConfig,
|
|
11
|
+
Diagnostic,
|
|
12
|
+
DiagnosticSeverity,
|
|
13
|
+
Location,
|
|
14
|
+
Position,
|
|
15
|
+
Range,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"LSPClient",
|
|
20
|
+
"LSPConfig",
|
|
21
|
+
"Diagnostic",
|
|
22
|
+
"DiagnosticSeverity",
|
|
23
|
+
"Location",
|
|
24
|
+
"Position",
|
|
25
|
+
"Range",
|
|
26
|
+
]
|