cade-cli 0.3.3__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.
- cade_cli-0.3.3.dist-info/METADATA +151 -0
- cade_cli-0.3.3.dist-info/RECORD +44 -0
- cade_cli-0.3.3.dist-info/WHEEL +4 -0
- cade_cli-0.3.3.dist-info/entry_points.txt +2 -0
- cadecoder/__init__.py +1 -0
- cadecoder/ai/__init__.py +6 -0
- cadecoder/ai/prompts.py +572 -0
- cadecoder/cli/__init__.py +0 -0
- cadecoder/cli/app.py +147 -0
- cadecoder/cli/auth.py +483 -0
- cadecoder/cli/commands/__init__.py +5 -0
- cadecoder/cli/commands/auth.py +143 -0
- cadecoder/cli/commands/chat.py +264 -0
- cadecoder/cli/commands/mcp.py +477 -0
- cadecoder/cli/commands/tools.py +226 -0
- cadecoder/core/__init__.py +12 -0
- cadecoder/core/config.py +380 -0
- cadecoder/core/constants.py +281 -0
- cadecoder/core/errors.py +145 -0
- cadecoder/core/logging.py +148 -0
- cadecoder/core/types.py +235 -0
- cadecoder/core/utils.py +279 -0
- cadecoder/execution/__init__.py +46 -0
- cadecoder/execution/context_window.py +521 -0
- cadecoder/execution/orchestrator.py +562 -0
- cadecoder/execution/parallel.py +287 -0
- cadecoder/providers/__init__.py +60 -0
- cadecoder/providers/base.py +294 -0
- cadecoder/providers/openai.py +251 -0
- cadecoder/storage/__init__.py +0 -0
- cadecoder/storage/threads.py +489 -0
- cadecoder/templates/login_failed.html +21 -0
- cadecoder/templates/login_success.html +21 -0
- cadecoder/templates/styles.css +87 -0
- cadecoder/tools/__init__.py +19 -0
- cadecoder/tools/builtin.py +644 -0
- cadecoder/tools/filesystem.py +315 -0
- cadecoder/tools/git.py +221 -0
- cadecoder/tools/manager.py +1635 -0
- cadecoder/ui/__init__.py +7 -0
- cadecoder/ui/display.py +338 -0
- cadecoder/ui/input.py +145 -0
- cadecoder/ui/session.py +455 -0
- cadecoder/ui/state.py +20 -0
cadecoder/core/errors.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Custom error types for CadeCoder.
|
|
2
|
+
|
|
3
|
+
This module defines a comprehensive exception hierarchy for the application.
|
|
4
|
+
All custom exceptions inherit from CadeCoderError, allowing for consistent
|
|
5
|
+
error handling throughout the codebase.
|
|
6
|
+
|
|
7
|
+
Error Hierarchy:
|
|
8
|
+
CadeCoderError (base)
|
|
9
|
+
├── CadeCoderFileNotFoundError
|
|
10
|
+
├── FileSystemError
|
|
11
|
+
├── FileOpsError
|
|
12
|
+
├── AnalysisError
|
|
13
|
+
│ └── DependencyError
|
|
14
|
+
├── ExecutionError
|
|
15
|
+
├── ConfigError
|
|
16
|
+
├── AuthError
|
|
17
|
+
├── AIError
|
|
18
|
+
├── StorageError
|
|
19
|
+
├── AgentExecutionError
|
|
20
|
+
└── PlanningError
|
|
21
|
+
├── PlanValidationError
|
|
22
|
+
└── PlanCreationError
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CadeCoderError(Exception):
|
|
27
|
+
"""Base class for all CadeCoder-specific errors.
|
|
28
|
+
|
|
29
|
+
All custom exceptions should inherit from this class to enable
|
|
30
|
+
consistent error handling and filtering.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class CadeCoderFileNotFoundError(CadeCoderError):
|
|
37
|
+
"""Raised when a required file or directory is not found."""
|
|
38
|
+
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# Backwards compatibility alias
|
|
43
|
+
FileNotFoundError = CadeCoderFileNotFoundError
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class FileSystemError(CadeCoderError):
|
|
47
|
+
"""Raised for general file system operations errors."""
|
|
48
|
+
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class FileOpsError(CadeCoderError):
|
|
53
|
+
"""Raised for errors during file operations like diffing or patching."""
|
|
54
|
+
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class AnalysisError(CadeCoderError):
|
|
59
|
+
"""Raised when an error occurs during codebase analysis or search."""
|
|
60
|
+
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class DependencyError(AnalysisError):
|
|
65
|
+
"""Raised when an error occurs during dependency parsing."""
|
|
66
|
+
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ExecutionError(CadeCoderError):
|
|
71
|
+
"""Raised when running an external command fails."""
|
|
72
|
+
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ConfigError(CadeCoderError):
|
|
77
|
+
"""Raised for configuration loading, validation, or saving errors."""
|
|
78
|
+
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AuthError(CadeCoderError):
|
|
83
|
+
"""Raised for authentication or API key related errors."""
|
|
84
|
+
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class AIError(CadeCoderError):
|
|
89
|
+
"""Raised for errors during interaction with the AI service."""
|
|
90
|
+
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class StorageError(CadeCoderError):
|
|
95
|
+
"""Raised for errors during storage operations."""
|
|
96
|
+
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class AgentExecutionError(CadeCoderError):
|
|
101
|
+
"""Raised when an agent fails to execute properly."""
|
|
102
|
+
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class PlanningError(CadeCoderError):
|
|
107
|
+
"""Base class for planning-related errors."""
|
|
108
|
+
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class PlanValidationError(PlanningError):
|
|
113
|
+
"""Raised when a plan fails schema validation.
|
|
114
|
+
|
|
115
|
+
Attributes:
|
|
116
|
+
schema_errors: List of specific schema validation error messages
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
def __init__(self, message: str, schema_errors: list[str] | None = None):
|
|
120
|
+
"""Initialize with error message and optional schema error details.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
message: Main error message
|
|
124
|
+
schema_errors: List of specific schema validation errors
|
|
125
|
+
"""
|
|
126
|
+
super().__init__(message)
|
|
127
|
+
self.schema_errors = schema_errors or []
|
|
128
|
+
|
|
129
|
+
def __str__(self) -> str:
|
|
130
|
+
"""Format error message with schema details.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Formatted error message including schema errors if present
|
|
134
|
+
"""
|
|
135
|
+
base_msg = super().__str__()
|
|
136
|
+
if self.schema_errors:
|
|
137
|
+
errors = "\n - ".join(self.schema_errors)
|
|
138
|
+
return f"{base_msg}\nSchema errors:\n - {errors}"
|
|
139
|
+
return base_msg
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class PlanCreationError(PlanningError):
|
|
143
|
+
"""Raised when plan creation fails after retries."""
|
|
144
|
+
|
|
145
|
+
pass
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Basic logging setup for CadeCoder.
|
|
2
|
+
|
|
3
|
+
This module provides structured logging with:
|
|
4
|
+
- File-based logging (rotating file handler)
|
|
5
|
+
- Chat thread context injection
|
|
6
|
+
- No console output (file-only)
|
|
7
|
+
|
|
8
|
+
The logging is initialized on import to ensure logs are captured
|
|
9
|
+
even before CLI arguments are parsed.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
# Set environment variable to disable tokenizers parallelism warning
|
|
15
|
+
# This must be set before any imports that use tokenizers
|
|
16
|
+
os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
from contextvars import ContextVar
|
|
20
|
+
from logging.handlers import RotatingFileHandler
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
from .config import config
|
|
24
|
+
|
|
25
|
+
# Get the logger instance first
|
|
26
|
+
log = logging.getLogger("cadecoder")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# --- Chat Thread Context ---
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
chat_thread_ctx: ContextVar[str] = ContextVar("chat_thread_ctx", default="-")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ChatThreadFilter(logging.Filter):
|
|
36
|
+
"""Inject chat thread name/id into log records as 'chat_thread'.
|
|
37
|
+
|
|
38
|
+
This filter adds thread context to all log records, enabling
|
|
39
|
+
filtering and searching logs by conversation thread.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
43
|
+
"""Filter log records to inject chat thread context.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
record: The log record to filter
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Always True (all records pass through, context is injected)
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
record.chat_thread = chat_thread_ctx.get()
|
|
53
|
+
except Exception:
|
|
54
|
+
record.chat_thread = "-"
|
|
55
|
+
return True
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# --- Log File Path ---
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_log_file_path() -> Path:
|
|
62
|
+
"""Get the path to the log file in the cadecoder app directory.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Path to the log file (creates parent directory if needed)
|
|
66
|
+
"""
|
|
67
|
+
app_dir = Path(config.app_dir)
|
|
68
|
+
app_dir.mkdir(parents=True, exist_ok=True)
|
|
69
|
+
return app_dir / "cadecoder.log"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# --- Logging Setup ---
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _create_file_handler(log_file: Path) -> RotatingFileHandler:
|
|
76
|
+
"""Create and configure rotating file handler.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
log_file: Path to log file
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Configured RotatingFileHandler
|
|
83
|
+
"""
|
|
84
|
+
handler = RotatingFileHandler(
|
|
85
|
+
log_file,
|
|
86
|
+
maxBytes=10 * 1024 * 1024, # 10MB
|
|
87
|
+
backupCount=3, # Keep 3 backup files
|
|
88
|
+
encoding="utf-8",
|
|
89
|
+
)
|
|
90
|
+
handler.setLevel(logging.DEBUG) # Always capture DEBUG in file
|
|
91
|
+
handler.addFilter(ChatThreadFilter())
|
|
92
|
+
|
|
93
|
+
# Detailed formatter for file logs
|
|
94
|
+
formatter = logging.Formatter(
|
|
95
|
+
"%(asctime)s - %(name)s - %(levelname)s - "
|
|
96
|
+
"[%(filename)s:%(lineno)d] - chat=%(chat_thread)s - %(message)s",
|
|
97
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
98
|
+
)
|
|
99
|
+
handler.setFormatter(formatter)
|
|
100
|
+
|
|
101
|
+
return handler
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _configure_root_logger() -> None:
|
|
105
|
+
"""Configure root logger to prevent console output from other libraries.
|
|
106
|
+
|
|
107
|
+
Sets root logger to WARNING level and removes all handlers to prevent
|
|
108
|
+
other libraries from outputting to console.
|
|
109
|
+
"""
|
|
110
|
+
root_logger = logging.getLogger()
|
|
111
|
+
root_logger.setLevel(logging.WARNING) # Only warnings and above
|
|
112
|
+
# Remove all handlers from root logger
|
|
113
|
+
for handler in root_logger.handlers[:]:
|
|
114
|
+
root_logger.removeHandler(handler)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def setup_logging(verbose: bool = False) -> None:
|
|
118
|
+
"""Set up file-only logging with no console output.
|
|
119
|
+
|
|
120
|
+
Side effect: configures logging system.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
verbose: If True, sets log level to DEBUG, otherwise INFO
|
|
124
|
+
"""
|
|
125
|
+
# Remove any existing handlers to avoid duplicates
|
|
126
|
+
for handler in log.handlers[:]:
|
|
127
|
+
log.removeHandler(handler)
|
|
128
|
+
|
|
129
|
+
log_level = logging.DEBUG if verbose else logging.INFO
|
|
130
|
+
log.setLevel(log_level)
|
|
131
|
+
|
|
132
|
+
# Create and add file handler
|
|
133
|
+
log_file = get_log_file_path()
|
|
134
|
+
file_handler = _create_file_handler(log_file)
|
|
135
|
+
log.addHandler(file_handler)
|
|
136
|
+
|
|
137
|
+
# Prevent propagation to root logger to avoid duplicate logs
|
|
138
|
+
log.propagate = False
|
|
139
|
+
|
|
140
|
+
# Configure root logger to prevent console output
|
|
141
|
+
_configure_root_logger()
|
|
142
|
+
|
|
143
|
+
log.debug(f"Logging initialized. Log file: {log_file}")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# Initialize logging with default settings on import
|
|
147
|
+
# This ensures logs go to file even before CLI args are parsed
|
|
148
|
+
setup_logging(verbose=False)
|
cadecoder/core/types.py
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Core shared types, enums, and message definitions for the entire codebase.
|
|
2
|
+
|
|
3
|
+
This module consolidates:
|
|
4
|
+
- Type enums (Role, ToolCallType, ExecutionEventType)
|
|
5
|
+
- TypedDict definitions for messages and tool calls
|
|
6
|
+
- Pydantic models for message structures
|
|
7
|
+
- Type aliases for common patterns
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Any, Literal
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
from typing_extensions import TypedDict
|
|
15
|
+
|
|
16
|
+
# ============================================================================
|
|
17
|
+
# Type Aliases
|
|
18
|
+
# ============================================================================
|
|
19
|
+
|
|
20
|
+
# Tool-related type aliases
|
|
21
|
+
ToolCallList = list[dict[str, Any]]
|
|
22
|
+
ToolResultTuple = tuple[str, str, str] # (call_id, tool_name, content)
|
|
23
|
+
ToolResultList = list[ToolResultTuple]
|
|
24
|
+
|
|
25
|
+
# Message-related type aliases
|
|
26
|
+
MessageDict = dict[str, Any]
|
|
27
|
+
MessageList = list[MessageDict]
|
|
28
|
+
ConversationHistory = list[MessageDict]
|
|
29
|
+
|
|
30
|
+
# Resource tracking for parallel execution
|
|
31
|
+
ResourceSet = set[str]
|
|
32
|
+
ToolGroup = list[dict[str, Any]]
|
|
33
|
+
ToolGroups = list[ToolGroup]
|
|
34
|
+
|
|
35
|
+
# ============================================================================
|
|
36
|
+
# Enums
|
|
37
|
+
# ============================================================================
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Role(str, Enum):
|
|
41
|
+
"""Message role types."""
|
|
42
|
+
|
|
43
|
+
USER = "user"
|
|
44
|
+
ASSISTANT = "assistant"
|
|
45
|
+
TOOL = "tool"
|
|
46
|
+
SYSTEM = "system"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ToolCallType(str, Enum):
|
|
50
|
+
"""Tool call type."""
|
|
51
|
+
|
|
52
|
+
FUNCTION = "function"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ExecutionEventType(str, Enum):
|
|
56
|
+
"""Event types for execution flow.
|
|
57
|
+
|
|
58
|
+
Values:
|
|
59
|
+
CONTENT: Text content from agent
|
|
60
|
+
TOOL_CALL: Tool call request
|
|
61
|
+
TOOL_EXECUTION_START: Tool execution started
|
|
62
|
+
TOOL_RESULT: Tool execution result
|
|
63
|
+
WARNING: Warning message
|
|
64
|
+
ERROR: Error message
|
|
65
|
+
COMPLETE: Execution complete
|
|
66
|
+
CONTEXT_COMPACTION: Context window was compacted
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
CONTENT = "content"
|
|
70
|
+
TOOL_CALL = "tool_call"
|
|
71
|
+
TOOL_EXECUTION_START = "tool_execution_start"
|
|
72
|
+
TOOL_RESULT = "tool_result"
|
|
73
|
+
WARNING = "warning"
|
|
74
|
+
ERROR = "error"
|
|
75
|
+
COMPLETE = "complete"
|
|
76
|
+
CONTEXT_COMPACTION = "context_compaction"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# ============================================================================
|
|
80
|
+
# Constants
|
|
81
|
+
# ============================================================================
|
|
82
|
+
|
|
83
|
+
# Prefixes for context/system scaffolding that should be excluded from user content
|
|
84
|
+
CONTEXT_PREFIXES: tuple[str, ...] = ("[Context:", "[Git Init")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# ============================================================================
|
|
88
|
+
# TypedDict Definitions (for compatibility)
|
|
89
|
+
# ============================================================================
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class ToolFunctionDict(TypedDict, total=False):
|
|
93
|
+
"""Tool function call structure."""
|
|
94
|
+
|
|
95
|
+
name: str
|
|
96
|
+
arguments: str
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class ToolCallShape(TypedDict, total=False):
|
|
100
|
+
"""Tool call shape structure."""
|
|
101
|
+
|
|
102
|
+
id: str
|
|
103
|
+
tool_call_id: str
|
|
104
|
+
type: str
|
|
105
|
+
function: ToolFunctionDict
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class BaseMessageDict(TypedDict, total=False):
|
|
109
|
+
"""Base message structure as TypedDict.
|
|
110
|
+
|
|
111
|
+
Used for compatibility with existing dict-based code.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
role: Literal["system", "user", "assistant", "tool"]
|
|
115
|
+
content: str | None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class ToolCallDict(TypedDict):
|
|
119
|
+
"""Tool call structure as TypedDict."""
|
|
120
|
+
|
|
121
|
+
id: str
|
|
122
|
+
type: Literal["function"]
|
|
123
|
+
function: dict[str, Any]
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class AssistantMessageDict(BaseMessageDict):
|
|
127
|
+
"""Assistant message with optional tool calls."""
|
|
128
|
+
|
|
129
|
+
tool_calls: list[ToolCallDict] | None
|
|
130
|
+
structured_state_json: str | None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class ToolMessageDict(BaseMessageDict):
|
|
134
|
+
"""Tool response message."""
|
|
135
|
+
|
|
136
|
+
tool_call_id: str
|
|
137
|
+
name: str | None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class ConversationMessageDict(TypedDict, total=False):
|
|
141
|
+
"""Union type for all message types in conversations."""
|
|
142
|
+
|
|
143
|
+
role: Literal["system", "user", "assistant", "tool"]
|
|
144
|
+
content: str | None
|
|
145
|
+
tool_calls: list[ToolCallDict] | None
|
|
146
|
+
tool_call_id: str | None
|
|
147
|
+
name: str | None
|
|
148
|
+
structured_state_json: str | None
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ============================================================================
|
|
152
|
+
# Pydantic Models
|
|
153
|
+
# ============================================================================
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class ToolExecutionResult(BaseModel):
|
|
157
|
+
"""Result from executing a single tool.
|
|
158
|
+
|
|
159
|
+
Attributes:
|
|
160
|
+
tool_call_id: ID of the tool call
|
|
161
|
+
name: Name of the tool that was executed
|
|
162
|
+
content: Result content or error message
|
|
163
|
+
status: Execution status (success or error)
|
|
164
|
+
error: Error message if status is error
|
|
165
|
+
authorization_url: Authorization URL if authorization is required
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
tool_call_id: str = Field(..., description="ID of the tool call")
|
|
169
|
+
name: str = Field(..., description="Name of the tool that was executed")
|
|
170
|
+
content: str = Field(..., description="Result content or error message")
|
|
171
|
+
status: Literal["success", "error"] = Field(..., description="Execution status")
|
|
172
|
+
error: str | None = Field(None, description="Error message if status is error")
|
|
173
|
+
authorization_url: str | None = Field(
|
|
174
|
+
None, description="Authorization URL if authorization is required"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class SingleMessageExitReason(str, Enum):
|
|
179
|
+
"""Exit reasons for single message mode.
|
|
180
|
+
|
|
181
|
+
Values:
|
|
182
|
+
COMPLETED: Task completed successfully
|
|
183
|
+
NEEDS_INPUT: Agent needs user input to continue
|
|
184
|
+
NEEDS_AUTH: Tool requires authorization
|
|
185
|
+
ERROR: Execution error occurred
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
COMPLETED = "completed"
|
|
189
|
+
NEEDS_INPUT = "needs_input"
|
|
190
|
+
NEEDS_AUTH = "needs_auth"
|
|
191
|
+
ERROR = "error"
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class SingleMessageResult(BaseModel):
|
|
195
|
+
"""Result from single message mode execution.
|
|
196
|
+
|
|
197
|
+
Attributes:
|
|
198
|
+
exit_reason: Why execution stopped
|
|
199
|
+
content: Final accumulated content
|
|
200
|
+
needs_interactive: Whether to transition to interactive mode
|
|
201
|
+
authorization_url: URL if auth is required
|
|
202
|
+
error_message: Error message if error occurred
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
exit_reason: SingleMessageExitReason
|
|
206
|
+
content: str = ""
|
|
207
|
+
needs_interactive: bool = False
|
|
208
|
+
authorization_url: str | None = None
|
|
209
|
+
error_message: str | None = None
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# ============================================================================
|
|
213
|
+
# Utility Functions
|
|
214
|
+
# ============================================================================
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def extract_tool_output_content(result: Any) -> str:
|
|
218
|
+
"""Extract content from tool execution result.
|
|
219
|
+
|
|
220
|
+
Handles various result formats:
|
|
221
|
+
- Objects with output.value attribute (Arcade response)
|
|
222
|
+
- Dicts with 'output' key
|
|
223
|
+
- Raw values (converted to string)
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
result: Raw tool execution result
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Extracted content as string
|
|
230
|
+
"""
|
|
231
|
+
if hasattr(result, "output") and hasattr(result.output, "value"):
|
|
232
|
+
return str(result.output.value)
|
|
233
|
+
if isinstance(result, dict) and "output" in result:
|
|
234
|
+
return str(result.get("output", result))
|
|
235
|
+
return str(result)
|