isa-agent-sdk 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.
- isa_agent_sdk/__init__.py +220 -0
- isa_agent_sdk/_hil.py +482 -0
- isa_agent_sdk/_messages.py +706 -0
- isa_agent_sdk/_query.py +964 -0
- isa_agent_sdk/_skills.py +570 -0
- isa_agent_sdk/_triggers.py +466 -0
- isa_agent_sdk/agent_client.py +792 -0
- isa_agent_sdk/agent_types/__init__.py +184 -0
- isa_agent_sdk/agent_types/agent_state.py +91 -0
- isa_agent_sdk/agent_types/common_types.py +332 -0
- isa_agent_sdk/agent_types/event_types.py +601 -0
- isa_agent_sdk/agent_types/hardware_types.py +218 -0
- isa_agent_sdk/agent_types/request_models.py +329 -0
- isa_agent_sdk/agent_types/response_models.py +525 -0
- isa_agent_sdk/clients/__init__.py +89 -0
- isa_agent_sdk/clients/base.py +206 -0
- isa_agent_sdk/clients/mcp_client.py +873 -0
- isa_agent_sdk/clients/model_client.py +581 -0
- isa_agent_sdk/clients/pool_manager_client.py +356 -0
- isa_agent_sdk/clients/session_client.py +844 -0
- isa_agent_sdk/clients/storage_client.py +1000 -0
- isa_agent_sdk/clients/user_client.py +446 -0
- isa_agent_sdk/components/__init__.py +23 -0
- isa_agent_sdk/components/billing_service.py +762 -0
- isa_agent_sdk/components/consul_discovery.py +112 -0
- isa_agent_sdk/components/multimodal_processor.py +416 -0
- isa_agent_sdk/components/session_service.py +321 -0
- isa_agent_sdk/components/storage_service.py +552 -0
- isa_agent_sdk/components/user_service.py +519 -0
- isa_agent_sdk/core/__init__.py +4 -0
- isa_agent_sdk/core/config/__init__.py +82 -0
- isa_agent_sdk/core/config/agent_config.py +329 -0
- isa_agent_sdk/core/config/consul_config.py +47 -0
- isa_agent_sdk/core/config/infra_config.py +148 -0
- isa_agent_sdk/core/config/lightning_config.py +89 -0
- isa_agent_sdk/core/config/logging_config.py +21 -0
- isa_agent_sdk/core/config/model_config.py +104 -0
- isa_agent_sdk/core/config/service_config.py +104 -0
- isa_agent_sdk/core/errors/__init__.py +3 -0
- isa_agent_sdk/core/errors/error_handler.py +240 -0
- isa_agent_sdk/core/resilience/__init__.py +4 -0
- isa_agent_sdk/core/resilience/circuit_breaker.py +205 -0
- isa_agent_sdk/core/resilience/connection_limiter.py +191 -0
- isa_agent_sdk/core/resilience/rate_limiter.py +202 -0
- isa_agent_sdk/core/resilience/system_protection.py +202 -0
- isa_agent_sdk/core/resilience/timeouts.py +123 -0
- isa_agent_sdk/graphs/__init__.py +14 -0
- isa_agent_sdk/graphs/base_graph.py +180 -0
- isa_agent_sdk/graphs/coding_graph.py +298 -0
- isa_agent_sdk/graphs/conversation_graph.py +127 -0
- isa_agent_sdk/graphs/graph_config_service.py +574 -0
- isa_agent_sdk/graphs/graph_registry_with_auth.py +581 -0
- isa_agent_sdk/graphs/graph_stream.py +252 -0
- isa_agent_sdk/graphs/research_graph.py +208 -0
- isa_agent_sdk/graphs/smart_agent_graph.py +917 -0
- isa_agent_sdk/graphs/utils/__init__.py +5 -0
- isa_agent_sdk/graphs/utils/context_init.py +1235 -0
- isa_agent_sdk/graphs/utils/context_schema.py +120 -0
- isa_agent_sdk/graphs/utils/context_update.py +117 -0
- isa_agent_sdk/graphs/utils/graph_lifecycle.py +267 -0
- isa_agent_sdk/graphs/utils/memory_utils.py +616 -0
- isa_agent_sdk/nodes/__init__.py +33 -0
- isa_agent_sdk/nodes/agent_executor_node.py +1099 -0
- isa_agent_sdk/nodes/agent_nodes/react_agent.py +298 -0
- isa_agent_sdk/nodes/base_node.py +727 -0
- isa_agent_sdk/nodes/docs/entry_node.md +275 -0
- isa_agent_sdk/nodes/docs/failsafe_node.md +314 -0
- isa_agent_sdk/nodes/docs/guardrail_node.md +550 -0
- isa_agent_sdk/nodes/docs/model_node.md +328 -0
- isa_agent_sdk/nodes/docs/response_node.md +0 -0
- isa_agent_sdk/nodes/docs/revise_node.md +441 -0
- isa_agent_sdk/nodes/docs/router_node.md +356 -0
- isa_agent_sdk/nodes/docs/tool_node.md +352 -0
- isa_agent_sdk/nodes/failsafe_node.py +650 -0
- isa_agent_sdk/nodes/guardrail_node.py +375 -0
- isa_agent_sdk/nodes/reason_node.py +665 -0
- isa_agent_sdk/nodes/response_node.py +339 -0
- isa_agent_sdk/nodes/sense_node.py +514 -0
- isa_agent_sdk/nodes/summarization_node.py +308 -0
- isa_agent_sdk/nodes/tool_node.py +655 -0
- isa_agent_sdk/nodes/utils/__init__.py +15 -0
- isa_agent_sdk/nodes/utils/artifact_detector.py +293 -0
- isa_agent_sdk/nodes/utils/background_hil_detector.py +349 -0
- isa_agent_sdk/nodes/utils/credential_auth.py +0 -0
- isa_agent_sdk/nodes/utils/event_trigger_manager.py +702 -0
- isa_agent_sdk/nodes/utils/oauth_auth.py +0 -0
- isa_agent_sdk/nodes/utils/tool_hil_detector.py +185 -0
- isa_agent_sdk/nodes/utils/tool_hil_router.py +182 -0
- isa_agent_sdk/options.py +409 -0
- isa_agent_sdk/services/auto_detection/DESIGN.md +1601 -0
- isa_agent_sdk/services/auto_detection/MCP_PROGRESS_INTEGRATION.md +473 -0
- isa_agent_sdk/services/auto_detection/README.md +197 -0
- isa_agent_sdk/services/auto_detection/tool_profiler.py +384 -0
- isa_agent_sdk/services/background_jobs/README.md +353 -0
- isa_agent_sdk/services/background_jobs/__init__.py +286 -0
- isa_agent_sdk/services/background_jobs/docs/DOCKER_TEST_RESULTS.md +340 -0
- isa_agent_sdk/services/background_jobs/docs/INTEGRATION_COMPLETE.md +454 -0
- isa_agent_sdk/services/background_jobs/docs/integration.md +225 -0
- isa_agent_sdk/services/background_jobs/nats_task_queue.py +372 -0
- isa_agent_sdk/services/background_jobs/redis_state_manager.py +362 -0
- isa_agent_sdk/services/background_jobs/task_models.py +105 -0
- isa_agent_sdk/services/background_jobs/task_worker.py +479 -0
- isa_agent_sdk/services/background_jobs/tests/test_task_approval.py +339 -0
- isa_agent_sdk/services/base_service.py +33 -0
- isa_agent_sdk/services/chat_service.py +1209 -0
- isa_agent_sdk/services/feedback/INDUSTRY_ANALYSIS.md +313 -0
- isa_agent_sdk/services/feedback/MIGRATION_SUMMARY.md +255 -0
- isa_agent_sdk/services/feedback/README.md +437 -0
- isa_agent_sdk/services/feedback/__init__.py +82 -0
- isa_agent_sdk/services/feedback/aggregator.py +297 -0
- isa_agent_sdk/services/feedback/analyzers/__init__.py +13 -0
- isa_agent_sdk/services/feedback/analyzers/pattern_analyzer.py +355 -0
- isa_agent_sdk/services/feedback/analyzers/semantic_analyzer.py +267 -0
- isa_agent_sdk/services/feedback/collectors.py +155 -0
- isa_agent_sdk/services/feedback/feedback_service.py +338 -0
- isa_agent_sdk/services/feedback/models.py +372 -0
- isa_agent_sdk/services/feedback/old/README.md +1 -0
- isa_agent_sdk/services/feedback/tests/test_feedback_service_mvp.py +217 -0
- isa_agent_sdk/services/hardware_service.py +299 -0
- isa_agent_sdk/services/human_in_the_loop/README.md +327 -0
- isa_agent_sdk/services/human_in_the_loop/__init__.py +132 -0
- isa_agent_sdk/services/human_in_the_loop/docs/TOOL_AUTH_HIL_FLOW_EXPLAINED.md +361 -0
- isa_agent_sdk/services/human_in_the_loop/hil_service.py +489 -0
- isa_agent_sdk/services/human_in_the_loop/interrupt_manager.py +343 -0
- isa_agent_sdk/services/human_in_the_loop/models.py +600 -0
- isa_agent_sdk/services/human_in_the_loop/old/FEATURE_COMPARISON.md +126 -0
- isa_agent_sdk/services/human_in_the_loop/old/README.md +1 -0
- isa_agent_sdk/services/human_in_the_loop/scenario_handlers.py +518 -0
- isa_agent_sdk/services/human_in_the_loop/tests/__init__.py +4 -0
- isa_agent_sdk/services/human_in_the_loop/tests/debug_failed_scenarios.py +165 -0
- isa_agent_sdk/services/human_in_the_loop/tests/test_hil_scenarios.py +683 -0
- isa_agent_sdk/services/human_in_the_loop/tests/test_hil_service.py +492 -0
- isa_agent_sdk/services/human_in_the_loop/tests/test_plan_tools_hil_auto.py +253 -0
- isa_agent_sdk/services/human_in_the_loop/tests/test_plan_tools_hil_integration.py +582 -0
- isa_agent_sdk/services/human_in_the_loop/tests/test_timeout_handler.py +373 -0
- isa_agent_sdk/services/human_in_the_loop/timeout_handler.py +290 -0
- isa_agent_sdk/services/human_in_the_loop/validators.py +237 -0
- isa_agent_sdk/services/lightning/__init__.py +35 -0
- isa_agent_sdk/services/lightning/adapter.py +163 -0
- isa_agent_sdk/services/lightning/lightning_store.py +111 -0
- isa_agent_sdk/services/lightning/service.py +375 -0
- isa_agent_sdk/services/lightning/tracer.py +178 -0
- isa_agent_sdk/services/persistence/__init__.py +31 -0
- isa_agent_sdk/services/persistence/durable_service.py +673 -0
- isa_agent_sdk/services/persistence/execution_manager.py +490 -0
- isa_agent_sdk/services/persistence/session_checkpointer.py +582 -0
- isa_agent_sdk/services/persistence/tests/check_manual_checkpoint.py +68 -0
- isa_agent_sdk/services/persistence/tests/check_recent_checkpoints.py +67 -0
- isa_agent_sdk/services/trace/__init__.py +25 -0
- isa_agent_sdk/services/trace/mcp_callback.py +252 -0
- isa_agent_sdk/services/trace/migrations/README.md +82 -0
- isa_agent_sdk/services/trace/model.py +0 -0
- isa_agent_sdk/services/trace/model_callback.py +288 -0
- isa_agent_sdk/services/trace/trace_writer.py +248 -0
- isa_agent_sdk/services/triggers/__init__.py +39 -0
- isa_agent_sdk/services/triggers/nats_event_bus.py +568 -0
- isa_agent_sdk/services/triggers/trigger_service.py +545 -0
- isa_agent_sdk/services/utils/stream_processor.py +993 -0
- isa_agent_sdk/utils/__init__.py +13 -0
- isa_agent_sdk/utils/logger.py +230 -0
- isa_agent_sdk/utils/task_manager.py +208 -0
- isa_agent_sdk-0.1.0.dist-info/METADATA +171 -0
- isa_agent_sdk-0.1.0.dist-info/RECORD +165 -0
- isa_agent_sdk-0.1.0.dist-info/WHEEL +5 -0
- isa_agent_sdk-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
isA Agent SDK
|
|
4
|
+
=============
|
|
5
|
+
|
|
6
|
+
A complete AI Agent SDK for building intelligent agents with advanced features.
|
|
7
|
+
Compatible with Claude Agent SDK patterns, with additional capabilities.
|
|
8
|
+
|
|
9
|
+
Quick Start:
|
|
10
|
+
from isa_agent_sdk import query, ISAAgentOptions
|
|
11
|
+
|
|
12
|
+
# Simple usage
|
|
13
|
+
async for msg in query("Hello, world!"):
|
|
14
|
+
print(msg.content)
|
|
15
|
+
|
|
16
|
+
# With options
|
|
17
|
+
async for msg in query(
|
|
18
|
+
"Fix the bug",
|
|
19
|
+
options=ISAAgentOptions(
|
|
20
|
+
allowed_tools=["Read", "Edit", "Bash"],
|
|
21
|
+
execution_mode="collaborative"
|
|
22
|
+
)
|
|
23
|
+
):
|
|
24
|
+
print(msg)
|
|
25
|
+
|
|
26
|
+
# Human-in-the-loop
|
|
27
|
+
from isa_agent_sdk import request_tool_permission, checkpoint
|
|
28
|
+
|
|
29
|
+
authorized = await request_tool_permission("delete_file", {"path": "data.txt"})
|
|
30
|
+
if authorized:
|
|
31
|
+
# proceed...
|
|
32
|
+
|
|
33
|
+
# HTTP Client (for deployed apps)
|
|
34
|
+
from isa_agent_sdk import ISAAgent
|
|
35
|
+
|
|
36
|
+
client = ISAAgent(base_url="http://localhost:8000")
|
|
37
|
+
response = client.chat.create(message="Hello!", user_id="user123")
|
|
38
|
+
|
|
39
|
+
Features:
|
|
40
|
+
- Claude Agent SDK-compatible API
|
|
41
|
+
- Streaming message interface
|
|
42
|
+
- Built-in tools (Read, Write, Edit, Bash, etc.)
|
|
43
|
+
- MCP server integration
|
|
44
|
+
- Skills system (prompt injection)
|
|
45
|
+
- Human-in-the-Loop (durable execution, survives restarts)
|
|
46
|
+
- Multiple execution modes (reactive, collaborative, proactive)
|
|
47
|
+
- Multiple environments (cloud_pool, cloud_shared, desktop)
|
|
48
|
+
- Event triggers (proactive agent activation)
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
# Version
|
|
52
|
+
__version__ = "0.1.0"
|
|
53
|
+
|
|
54
|
+
# Configuration options (no circular imports)
|
|
55
|
+
from .options import (
|
|
56
|
+
ISAAgentOptions,
|
|
57
|
+
Options, # Alias
|
|
58
|
+
ExecutionEnv,
|
|
59
|
+
ExecutionMode,
|
|
60
|
+
ToolDiscoveryMode,
|
|
61
|
+
PermissionMode,
|
|
62
|
+
GuardrailMode,
|
|
63
|
+
MCPServerConfig,
|
|
64
|
+
AgentDefinition,
|
|
65
|
+
HookMatcher,
|
|
66
|
+
PoolConfig,
|
|
67
|
+
TriggerConfig,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# HTTP Client (standalone, no circular imports)
|
|
71
|
+
from .agent_client import (
|
|
72
|
+
ISAAgent,
|
|
73
|
+
ISAAgentSync,
|
|
74
|
+
AgentEvent,
|
|
75
|
+
AgentResponse,
|
|
76
|
+
SessionInfo,
|
|
77
|
+
EventType,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# Cache for lazily imported modules
|
|
82
|
+
_lazy_modules = {}
|
|
83
|
+
|
|
84
|
+
# Lazy imports for modules with complex dependencies
|
|
85
|
+
def __getattr__(name):
|
|
86
|
+
"""Lazy import for modules with circular dependency potential"""
|
|
87
|
+
import importlib
|
|
88
|
+
|
|
89
|
+
# Core query functions
|
|
90
|
+
if name in ("query", "query_sync", "ask", "ask_sync", "execute_tool",
|
|
91
|
+
"get_available_tools", "get_session_state", "resume",
|
|
92
|
+
"resume_sync", "QueryExecutor"):
|
|
93
|
+
if "_query" not in _lazy_modules:
|
|
94
|
+
_lazy_modules["_query"] = importlib.import_module("._query", "isa_agent_sdk")
|
|
95
|
+
return getattr(_lazy_modules["_query"], name)
|
|
96
|
+
|
|
97
|
+
# Message types
|
|
98
|
+
if name in ("AgentMessage", "ConversationHistory", "ISAEventType",
|
|
99
|
+
"EventData", "EventEmitter", "EventCategory",
|
|
100
|
+
"MESSAGE_TYPE_MAP", "REVERSE_TYPE_MAP"):
|
|
101
|
+
if "_messages" not in _lazy_modules:
|
|
102
|
+
_lazy_modules["_messages"] = importlib.import_module("._messages", "isa_agent_sdk")
|
|
103
|
+
return getattr(_lazy_modules["_messages"], name)
|
|
104
|
+
|
|
105
|
+
# Human-in-the-Loop
|
|
106
|
+
if name in ("get_hil", "request_tool_permission", "request_authorization",
|
|
107
|
+
"collect_input", "collect_credentials", "collect_selection",
|
|
108
|
+
"request_review", "request_plan_approval", "request_execution_choice",
|
|
109
|
+
"checkpoint", "get_hil_stats", "clear_hil_history"):
|
|
110
|
+
if "_hil" not in _lazy_modules:
|
|
111
|
+
_lazy_modules["_hil"] = importlib.import_module("._hil", "isa_agent_sdk")
|
|
112
|
+
return getattr(_lazy_modules["_hil"], name)
|
|
113
|
+
|
|
114
|
+
# Skills system
|
|
115
|
+
if name in ("Skill", "SkillManager", "BUILTIN_SKILLS", "get_skill_manager",
|
|
116
|
+
"load_skill", "load_builtin_skill", "activate_skills",
|
|
117
|
+
"get_skill_injection", "list_builtin_skills"):
|
|
118
|
+
if "_skills" not in _lazy_modules:
|
|
119
|
+
_lazy_modules["_skills"] = importlib.import_module("._skills", "isa_agent_sdk")
|
|
120
|
+
return getattr(_lazy_modules["_skills"], name)
|
|
121
|
+
|
|
122
|
+
# Event Triggers
|
|
123
|
+
if name in ("TriggerType", "TriggerCondition", "register_trigger",
|
|
124
|
+
"unregister_trigger", "get_user_triggers", "get_trigger_stats",
|
|
125
|
+
"initialize_triggers", "shutdown_triggers", "get_trigger_manager",
|
|
126
|
+
"register_price_trigger", "register_schedule_trigger",
|
|
127
|
+
"register_event_pattern_trigger"):
|
|
128
|
+
if "_triggers" not in _lazy_modules:
|
|
129
|
+
_lazy_modules["_triggers"] = importlib.import_module("._triggers", "isa_agent_sdk")
|
|
130
|
+
return getattr(_lazy_modules["_triggers"], name)
|
|
131
|
+
|
|
132
|
+
raise AttributeError(f"module 'isa_agent_sdk' has no attribute '{name}'")
|
|
133
|
+
|
|
134
|
+
__all__ = [
|
|
135
|
+
# Version
|
|
136
|
+
"__version__",
|
|
137
|
+
|
|
138
|
+
# Core functions
|
|
139
|
+
"query",
|
|
140
|
+
"query_sync",
|
|
141
|
+
"ask",
|
|
142
|
+
"ask_sync",
|
|
143
|
+
"execute_tool",
|
|
144
|
+
"get_available_tools",
|
|
145
|
+
"get_session_state",
|
|
146
|
+
"resume",
|
|
147
|
+
"resume_sync",
|
|
148
|
+
"QueryExecutor",
|
|
149
|
+
|
|
150
|
+
# Options
|
|
151
|
+
"ISAAgentOptions",
|
|
152
|
+
"Options",
|
|
153
|
+
"ExecutionEnv",
|
|
154
|
+
"ExecutionMode",
|
|
155
|
+
"ToolDiscoveryMode",
|
|
156
|
+
"PermissionMode",
|
|
157
|
+
"GuardrailMode",
|
|
158
|
+
"MCPServerConfig",
|
|
159
|
+
"AgentDefinition",
|
|
160
|
+
"HookMatcher",
|
|
161
|
+
"PoolConfig",
|
|
162
|
+
"TriggerConfig",
|
|
163
|
+
|
|
164
|
+
# Messages
|
|
165
|
+
"AgentMessage",
|
|
166
|
+
"ConversationHistory",
|
|
167
|
+
"ISAEventType",
|
|
168
|
+
"EventData",
|
|
169
|
+
"EventEmitter",
|
|
170
|
+
"EventCategory",
|
|
171
|
+
"MESSAGE_TYPE_MAP",
|
|
172
|
+
"REVERSE_TYPE_MAP",
|
|
173
|
+
|
|
174
|
+
# Human-in-the-Loop
|
|
175
|
+
"get_hil",
|
|
176
|
+
"request_tool_permission",
|
|
177
|
+
"request_authorization",
|
|
178
|
+
"collect_input",
|
|
179
|
+
"collect_credentials",
|
|
180
|
+
"collect_selection",
|
|
181
|
+
"request_review",
|
|
182
|
+
"request_plan_approval",
|
|
183
|
+
"request_execution_choice",
|
|
184
|
+
"checkpoint",
|
|
185
|
+
"get_hil_stats",
|
|
186
|
+
"clear_hil_history",
|
|
187
|
+
|
|
188
|
+
# Skills
|
|
189
|
+
"Skill",
|
|
190
|
+
"SkillManager",
|
|
191
|
+
"BUILTIN_SKILLS",
|
|
192
|
+
"get_skill_manager",
|
|
193
|
+
"load_skill",
|
|
194
|
+
"load_builtin_skill",
|
|
195
|
+
"activate_skills",
|
|
196
|
+
"get_skill_injection",
|
|
197
|
+
"list_builtin_skills",
|
|
198
|
+
|
|
199
|
+
# Event Triggers
|
|
200
|
+
"TriggerType",
|
|
201
|
+
"TriggerCondition",
|
|
202
|
+
"register_trigger",
|
|
203
|
+
"unregister_trigger",
|
|
204
|
+
"get_user_triggers",
|
|
205
|
+
"get_trigger_stats",
|
|
206
|
+
"initialize_triggers",
|
|
207
|
+
"shutdown_triggers",
|
|
208
|
+
"get_trigger_manager",
|
|
209
|
+
"register_price_trigger",
|
|
210
|
+
"register_schedule_trigger",
|
|
211
|
+
"register_event_pattern_trigger",
|
|
212
|
+
|
|
213
|
+
# HTTP Client
|
|
214
|
+
"ISAAgent",
|
|
215
|
+
"ISAAgentSync",
|
|
216
|
+
"AgentEvent",
|
|
217
|
+
"AgentResponse",
|
|
218
|
+
"SessionInfo",
|
|
219
|
+
"EventType",
|
|
220
|
+
]
|
isa_agent_sdk/_hil.py
ADDED
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
isA Agent SDK - Human-in-the-Loop Integration
|
|
4
|
+
==============================================
|
|
5
|
+
|
|
6
|
+
SDK wrapper around the existing HIL service.
|
|
7
|
+
Provides Claude SDK-compatible interface while using isA's powerful
|
|
8
|
+
durable execution HIL system underneath.
|
|
9
|
+
|
|
10
|
+
isA's HIL is MORE powerful than Claude SDK hooks:
|
|
11
|
+
- Durable execution (survives restarts via LangGraph checkpointing)
|
|
12
|
+
- Async human responses (can wait hours/days)
|
|
13
|
+
- Full scenarios: authorization, review, input validation with retry
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
from isa_agent_sdk import query, ISAAgentOptions
|
|
17
|
+
from isa_agent_sdk.hil import request_tool_permission, collect_input
|
|
18
|
+
|
|
19
|
+
# Request permission before dangerous operation
|
|
20
|
+
authorized = await request_tool_permission(
|
|
21
|
+
tool_name="delete_file",
|
|
22
|
+
tool_args={"path": "/important/data.txt"},
|
|
23
|
+
reason="User requested file deletion"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
if authorized:
|
|
27
|
+
# Proceed with operation
|
|
28
|
+
...
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from typing import Dict, Any, Optional, List
|
|
32
|
+
|
|
33
|
+
# Import the existing HIL service - REUSE, don't duplicate!
|
|
34
|
+
from .services.human_in_the_loop import get_hil_service, HILService
|
|
35
|
+
from .services.human_in_the_loop.models import InterruptStats
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_hil() -> HILService:
|
|
39
|
+
"""
|
|
40
|
+
Get the HIL service instance.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
HILService singleton instance
|
|
44
|
+
"""
|
|
45
|
+
return get_hil_service()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# === Authorization Methods ===
|
|
49
|
+
|
|
50
|
+
async def request_tool_permission(
|
|
51
|
+
tool_name: str,
|
|
52
|
+
tool_args: Dict[str, Any],
|
|
53
|
+
security_level: str = "HIGH",
|
|
54
|
+
reason: Optional[str] = None,
|
|
55
|
+
user_id: str = "default"
|
|
56
|
+
) -> bool:
|
|
57
|
+
"""
|
|
58
|
+
Request permission to execute a tool.
|
|
59
|
+
|
|
60
|
+
This pauses graph execution until human responds.
|
|
61
|
+
Uses durable execution - survives process restarts.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
tool_name: Name of tool requesting permission
|
|
65
|
+
tool_args: Arguments for the tool
|
|
66
|
+
security_level: Security level (LOW, MEDIUM, HIGH, CRITICAL)
|
|
67
|
+
reason: Reason for needing this tool
|
|
68
|
+
user_id: User identifier
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
True if authorized, False if rejected
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
authorized = await request_tool_permission(
|
|
75
|
+
tool_name="web_crawl",
|
|
76
|
+
tool_args={"url": "https://example.com"},
|
|
77
|
+
security_level="HIGH",
|
|
78
|
+
reason="Need to fetch page content"
|
|
79
|
+
)
|
|
80
|
+
"""
|
|
81
|
+
hil = get_hil_service()
|
|
82
|
+
return await hil.request_authorization(
|
|
83
|
+
scenario="tool_authorization",
|
|
84
|
+
data={
|
|
85
|
+
"action": f"Execute tool: {tool_name}",
|
|
86
|
+
"tool_name": tool_name,
|
|
87
|
+
"tool_args": tool_args,
|
|
88
|
+
"reason": reason or f"Tool {tool_name} requires authorization",
|
|
89
|
+
"risk_level": security_level.lower(),
|
|
90
|
+
"context": {"tool_name": tool_name, "args": tool_args}
|
|
91
|
+
},
|
|
92
|
+
user_id=user_id,
|
|
93
|
+
node_source="sdk"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
async def request_authorization(
|
|
98
|
+
action: str,
|
|
99
|
+
reason: str,
|
|
100
|
+
context: Optional[Dict[str, Any]] = None,
|
|
101
|
+
risk_level: str = "medium",
|
|
102
|
+
user_id: str = "default"
|
|
103
|
+
) -> bool:
|
|
104
|
+
"""
|
|
105
|
+
Request generic authorization for an action.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
action: Description of the action
|
|
109
|
+
reason: Why authorization is needed
|
|
110
|
+
context: Additional context
|
|
111
|
+
risk_level: Risk level (low, medium, high, critical)
|
|
112
|
+
user_id: User identifier
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
True if authorized, False if rejected
|
|
116
|
+
|
|
117
|
+
Example:
|
|
118
|
+
authorized = await request_authorization(
|
|
119
|
+
action="Delete all temporary files",
|
|
120
|
+
reason="Cleanup requested by user",
|
|
121
|
+
risk_level="high"
|
|
122
|
+
)
|
|
123
|
+
"""
|
|
124
|
+
hil = get_hil_service()
|
|
125
|
+
return await hil.request_authorization(
|
|
126
|
+
scenario="generic",
|
|
127
|
+
data={
|
|
128
|
+
"action": action,
|
|
129
|
+
"reason": reason,
|
|
130
|
+
"risk_level": risk_level,
|
|
131
|
+
"context": context or {}
|
|
132
|
+
},
|
|
133
|
+
user_id=user_id,
|
|
134
|
+
node_source="sdk"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# === Input Collection Methods ===
|
|
139
|
+
|
|
140
|
+
async def collect_input(
|
|
141
|
+
prompt: str,
|
|
142
|
+
description: Optional[str] = None,
|
|
143
|
+
input_type: str = "text",
|
|
144
|
+
schema: Optional[Dict[str, Any]] = None,
|
|
145
|
+
user_id: str = "default"
|
|
146
|
+
) -> Any:
|
|
147
|
+
"""
|
|
148
|
+
Collect input from the user.
|
|
149
|
+
|
|
150
|
+
Pauses execution until user provides input.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
prompt: Question/prompt for the user
|
|
154
|
+
description: Additional description
|
|
155
|
+
input_type: Type of input (text, number, boolean, selection)
|
|
156
|
+
schema: JSON schema for validation
|
|
157
|
+
user_id: User identifier
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
User's input (validated if schema provided)
|
|
161
|
+
|
|
162
|
+
Example:
|
|
163
|
+
email = await collect_input(
|
|
164
|
+
prompt="What is your email address?",
|
|
165
|
+
input_type="text",
|
|
166
|
+
schema={"type": "string", "format": "email"}
|
|
167
|
+
)
|
|
168
|
+
"""
|
|
169
|
+
hil = get_hil_service()
|
|
170
|
+
return await hil.request_input(
|
|
171
|
+
scenario="generic",
|
|
172
|
+
data={
|
|
173
|
+
"prompt": prompt,
|
|
174
|
+
"description": description or prompt,
|
|
175
|
+
"input_type": input_type,
|
|
176
|
+
"schema": schema
|
|
177
|
+
},
|
|
178
|
+
user_id=user_id,
|
|
179
|
+
node_source="sdk"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
async def collect_credentials(
|
|
184
|
+
prompt: str,
|
|
185
|
+
credential_type: str = "api_key",
|
|
186
|
+
description: Optional[str] = None,
|
|
187
|
+
user_id: str = "default"
|
|
188
|
+
) -> str:
|
|
189
|
+
"""
|
|
190
|
+
Collect credentials from the user.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
prompt: Prompt for credentials
|
|
194
|
+
credential_type: Type (api_key, password, token)
|
|
195
|
+
description: Additional description
|
|
196
|
+
user_id: User identifier
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Credential string
|
|
200
|
+
|
|
201
|
+
Example:
|
|
202
|
+
api_key = await collect_credentials(
|
|
203
|
+
prompt="Enter your OpenAI API key",
|
|
204
|
+
credential_type="api_key"
|
|
205
|
+
)
|
|
206
|
+
"""
|
|
207
|
+
hil = get_hil_service()
|
|
208
|
+
return await hil.request_input(
|
|
209
|
+
scenario="credentials",
|
|
210
|
+
data={
|
|
211
|
+
"input_type": "credentials",
|
|
212
|
+
"prompt": prompt,
|
|
213
|
+
"description": description or prompt,
|
|
214
|
+
"credential_type": credential_type
|
|
215
|
+
},
|
|
216
|
+
user_id=user_id,
|
|
217
|
+
node_source="sdk"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
async def collect_selection(
|
|
222
|
+
prompt: str,
|
|
223
|
+
options: List[str],
|
|
224
|
+
description: Optional[str] = None,
|
|
225
|
+
user_id: str = "default"
|
|
226
|
+
) -> str:
|
|
227
|
+
"""
|
|
228
|
+
Collect a selection from predefined options.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
prompt: Question for the user
|
|
232
|
+
options: List of options to choose from
|
|
233
|
+
description: Additional description
|
|
234
|
+
user_id: User identifier
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
Selected option
|
|
238
|
+
|
|
239
|
+
Example:
|
|
240
|
+
choice = await collect_selection(
|
|
241
|
+
prompt="Which database should I use?",
|
|
242
|
+
options=["PostgreSQL", "MySQL", "SQLite"]
|
|
243
|
+
)
|
|
244
|
+
"""
|
|
245
|
+
hil = get_hil_service()
|
|
246
|
+
return await hil.request_input(
|
|
247
|
+
scenario="selection",
|
|
248
|
+
data={
|
|
249
|
+
"input_type": "selection",
|
|
250
|
+
"prompt": prompt,
|
|
251
|
+
"description": description or prompt,
|
|
252
|
+
"options": options
|
|
253
|
+
},
|
|
254
|
+
user_id=user_id,
|
|
255
|
+
node_source="sdk"
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
# === Review Methods ===
|
|
260
|
+
|
|
261
|
+
async def request_review(
|
|
262
|
+
content: str,
|
|
263
|
+
content_type: str = "text",
|
|
264
|
+
instructions: Optional[str] = None,
|
|
265
|
+
editable: bool = True,
|
|
266
|
+
user_id: str = "default"
|
|
267
|
+
) -> Dict[str, Any]:
|
|
268
|
+
"""
|
|
269
|
+
Request human review of content.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
content: Content to review
|
|
273
|
+
content_type: Type (text, code, plan, config)
|
|
274
|
+
instructions: Review instructions
|
|
275
|
+
editable: Whether user can edit
|
|
276
|
+
user_id: User identifier
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
Dict with 'approved' (bool), 'edited_content' (if edited), 'action' (str)
|
|
280
|
+
|
|
281
|
+
Example:
|
|
282
|
+
result = await request_review(
|
|
283
|
+
content=generated_code,
|
|
284
|
+
content_type="code",
|
|
285
|
+
instructions="Review this code before execution",
|
|
286
|
+
editable=True
|
|
287
|
+
)
|
|
288
|
+
if result['approved']:
|
|
289
|
+
code = result.get('edited_content', generated_code)
|
|
290
|
+
"""
|
|
291
|
+
hil = get_hil_service()
|
|
292
|
+
return await hil.request_review(
|
|
293
|
+
scenario=content_type if content_type in ["code", "config"] else "generic",
|
|
294
|
+
data={
|
|
295
|
+
"content": content,
|
|
296
|
+
"content_type": content_type,
|
|
297
|
+
"instructions": instructions or f"Please review this {content_type}",
|
|
298
|
+
"editable": editable
|
|
299
|
+
},
|
|
300
|
+
user_id=user_id,
|
|
301
|
+
node_source="sdk"
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
async def request_plan_approval(
|
|
306
|
+
plan: str,
|
|
307
|
+
plan_title: Optional[str] = None,
|
|
308
|
+
user_id: str = "default"
|
|
309
|
+
) -> Dict[str, Any]:
|
|
310
|
+
"""
|
|
311
|
+
Request approval for an execution plan.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
plan: The plan content (text or JSON)
|
|
315
|
+
plan_title: Title for the plan
|
|
316
|
+
user_id: User identifier
|
|
317
|
+
|
|
318
|
+
Returns:
|
|
319
|
+
Dict with 'approved' (bool), 'edited_content' (if edited)
|
|
320
|
+
|
|
321
|
+
Example:
|
|
322
|
+
result = await request_plan_approval(
|
|
323
|
+
plan="1. Read file\\n2. Parse data\\n3. Update database",
|
|
324
|
+
plan_title="Data Migration Plan"
|
|
325
|
+
)
|
|
326
|
+
"""
|
|
327
|
+
hil = get_hil_service()
|
|
328
|
+
return await hil.request_review(
|
|
329
|
+
scenario="execution_plan",
|
|
330
|
+
data={
|
|
331
|
+
"content": plan,
|
|
332
|
+
"content_type": "execution_plan",
|
|
333
|
+
"instructions": f"Review execution plan: {plan_title or 'Untitled'}",
|
|
334
|
+
"editable": True
|
|
335
|
+
},
|
|
336
|
+
user_id=user_id,
|
|
337
|
+
node_source="sdk"
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# === Execution Choice ===
|
|
342
|
+
|
|
343
|
+
async def request_execution_choice(
|
|
344
|
+
prompt: str,
|
|
345
|
+
options: List[Dict[str, str]],
|
|
346
|
+
estimated_time_seconds: Optional[float] = None,
|
|
347
|
+
recommendation: Optional[str] = None,
|
|
348
|
+
user_id: str = "default"
|
|
349
|
+
) -> str:
|
|
350
|
+
"""
|
|
351
|
+
Request user choice for execution strategy.
|
|
352
|
+
|
|
353
|
+
Useful for long-running tasks where user can choose
|
|
354
|
+
between quick/comprehensive/background execution.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
prompt: Description of the situation
|
|
358
|
+
options: List of options with 'value', 'label', 'description'
|
|
359
|
+
estimated_time_seconds: Estimated execution time
|
|
360
|
+
recommendation: Recommended option value
|
|
361
|
+
user_id: User identifier
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
Selected option value
|
|
365
|
+
|
|
366
|
+
Example:
|
|
367
|
+
choice = await request_execution_choice(
|
|
368
|
+
prompt="This task will take ~60 seconds",
|
|
369
|
+
options=[
|
|
370
|
+
{"value": "quick", "label": "Quick", "description": "Fast (~30s)"},
|
|
371
|
+
{"value": "full", "label": "Full", "description": "Complete (~60s)"},
|
|
372
|
+
{"value": "background", "label": "Background", "description": "Run async"}
|
|
373
|
+
],
|
|
374
|
+
recommendation="background"
|
|
375
|
+
)
|
|
376
|
+
"""
|
|
377
|
+
hil = get_hil_service()
|
|
378
|
+
return await hil.request_execution_choice(
|
|
379
|
+
scenario="long_running_task",
|
|
380
|
+
data={
|
|
381
|
+
"prompt": prompt,
|
|
382
|
+
"options": options,
|
|
383
|
+
"estimated_time_seconds": estimated_time_seconds,
|
|
384
|
+
"recommendation": recommendation
|
|
385
|
+
},
|
|
386
|
+
user_id=user_id,
|
|
387
|
+
node_source="sdk"
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
# === Checkpoint (Collaborative Mode) ===
|
|
392
|
+
|
|
393
|
+
async def checkpoint(
|
|
394
|
+
message: str,
|
|
395
|
+
context: Optional[Dict[str, Any]] = None,
|
|
396
|
+
require_approval: bool = True,
|
|
397
|
+
user_id: str = "default"
|
|
398
|
+
) -> bool:
|
|
399
|
+
"""
|
|
400
|
+
Create a checkpoint in collaborative mode.
|
|
401
|
+
|
|
402
|
+
Pauses execution for human review/approval before continuing.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
message: Checkpoint message/question
|
|
406
|
+
context: Additional context to show
|
|
407
|
+
require_approval: Whether to require explicit approval
|
|
408
|
+
user_id: User identifier
|
|
409
|
+
|
|
410
|
+
Returns:
|
|
411
|
+
True if approved to continue, False otherwise
|
|
412
|
+
|
|
413
|
+
Example:
|
|
414
|
+
# In collaborative mode, checkpoint every N steps
|
|
415
|
+
approved = await checkpoint(
|
|
416
|
+
message="Completed step 3. Ready to proceed?",
|
|
417
|
+
context={"completed": ["step1", "step2", "step3"]}
|
|
418
|
+
)
|
|
419
|
+
"""
|
|
420
|
+
if not require_approval:
|
|
421
|
+
return True
|
|
422
|
+
|
|
423
|
+
hil = get_hil_service()
|
|
424
|
+
return await hil.request_authorization(
|
|
425
|
+
scenario="generic",
|
|
426
|
+
data={
|
|
427
|
+
"action": "Continue execution",
|
|
428
|
+
"reason": message,
|
|
429
|
+
"risk_level": "low",
|
|
430
|
+
"context": context or {}
|
|
431
|
+
},
|
|
432
|
+
user_id=user_id,
|
|
433
|
+
node_source="sdk_checkpoint"
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
# === Statistics ===
|
|
438
|
+
|
|
439
|
+
def get_hil_stats() -> InterruptStats:
|
|
440
|
+
"""
|
|
441
|
+
Get HIL interrupt statistics.
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
InterruptStats with total, by_type, by_node, latest
|
|
445
|
+
"""
|
|
446
|
+
hil = get_hil_service()
|
|
447
|
+
return hil.get_interrupt_stats()
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def clear_hil_history() -> None:
|
|
451
|
+
"""Clear HIL interrupt history"""
|
|
452
|
+
hil = get_hil_service()
|
|
453
|
+
hil.clear_interrupt_history()
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
__all__ = [
|
|
457
|
+
# Service access
|
|
458
|
+
"get_hil",
|
|
459
|
+
|
|
460
|
+
# Authorization
|
|
461
|
+
"request_tool_permission",
|
|
462
|
+
"request_authorization",
|
|
463
|
+
|
|
464
|
+
# Input collection
|
|
465
|
+
"collect_input",
|
|
466
|
+
"collect_credentials",
|
|
467
|
+
"collect_selection",
|
|
468
|
+
|
|
469
|
+
# Review
|
|
470
|
+
"request_review",
|
|
471
|
+
"request_plan_approval",
|
|
472
|
+
|
|
473
|
+
# Execution choice
|
|
474
|
+
"request_execution_choice",
|
|
475
|
+
|
|
476
|
+
# Collaborative mode
|
|
477
|
+
"checkpoint",
|
|
478
|
+
|
|
479
|
+
# Statistics
|
|
480
|
+
"get_hil_stats",
|
|
481
|
+
"clear_hil_history",
|
|
482
|
+
]
|