alita-sdk 0.3.379__py3-none-any.whl → 0.3.627__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.
- alita_sdk/cli/__init__.py +10 -0
- alita_sdk/cli/__main__.py +17 -0
- alita_sdk/cli/agent/__init__.py +5 -0
- alita_sdk/cli/agent/default.py +258 -0
- alita_sdk/cli/agent_executor.py +156 -0
- alita_sdk/cli/agent_loader.py +245 -0
- alita_sdk/cli/agent_ui.py +228 -0
- alita_sdk/cli/agents.py +3113 -0
- alita_sdk/cli/callbacks.py +647 -0
- alita_sdk/cli/cli.py +168 -0
- alita_sdk/cli/config.py +306 -0
- alita_sdk/cli/context/__init__.py +30 -0
- alita_sdk/cli/context/cleanup.py +198 -0
- alita_sdk/cli/context/manager.py +731 -0
- alita_sdk/cli/context/message.py +285 -0
- alita_sdk/cli/context/strategies.py +289 -0
- alita_sdk/cli/context/token_estimation.py +127 -0
- alita_sdk/cli/formatting.py +182 -0
- alita_sdk/cli/input_handler.py +419 -0
- alita_sdk/cli/inventory.py +1073 -0
- alita_sdk/cli/mcp_loader.py +315 -0
- alita_sdk/cli/testcases/__init__.py +94 -0
- alita_sdk/cli/testcases/data_generation.py +119 -0
- alita_sdk/cli/testcases/discovery.py +96 -0
- alita_sdk/cli/testcases/executor.py +84 -0
- alita_sdk/cli/testcases/logger.py +85 -0
- alita_sdk/cli/testcases/parser.py +172 -0
- alita_sdk/cli/testcases/prompts.py +91 -0
- alita_sdk/cli/testcases/reporting.py +125 -0
- alita_sdk/cli/testcases/setup.py +108 -0
- alita_sdk/cli/testcases/test_runner.py +282 -0
- alita_sdk/cli/testcases/utils.py +39 -0
- alita_sdk/cli/testcases/validation.py +90 -0
- alita_sdk/cli/testcases/workflow.py +196 -0
- alita_sdk/cli/toolkit.py +327 -0
- alita_sdk/cli/toolkit_loader.py +85 -0
- alita_sdk/cli/tools/__init__.py +43 -0
- alita_sdk/cli/tools/approval.py +224 -0
- alita_sdk/cli/tools/filesystem.py +1751 -0
- alita_sdk/cli/tools/planning.py +389 -0
- alita_sdk/cli/tools/terminal.py +414 -0
- alita_sdk/community/__init__.py +72 -12
- alita_sdk/community/inventory/__init__.py +236 -0
- alita_sdk/community/inventory/config.py +257 -0
- alita_sdk/community/inventory/enrichment.py +2137 -0
- alita_sdk/community/inventory/extractors.py +1469 -0
- alita_sdk/community/inventory/ingestion.py +3172 -0
- alita_sdk/community/inventory/knowledge_graph.py +1457 -0
- alita_sdk/community/inventory/parsers/__init__.py +218 -0
- alita_sdk/community/inventory/parsers/base.py +295 -0
- alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
- alita_sdk/community/inventory/parsers/go_parser.py +851 -0
- alita_sdk/community/inventory/parsers/html_parser.py +389 -0
- alita_sdk/community/inventory/parsers/java_parser.py +593 -0
- alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
- alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
- alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
- alita_sdk/community/inventory/parsers/python_parser.py +604 -0
- alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
- alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
- alita_sdk/community/inventory/parsers/text_parser.py +322 -0
- alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
- alita_sdk/community/inventory/patterns/__init__.py +61 -0
- alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
- alita_sdk/community/inventory/patterns/loader.py +348 -0
- alita_sdk/community/inventory/patterns/registry.py +198 -0
- alita_sdk/community/inventory/presets.py +535 -0
- alita_sdk/community/inventory/retrieval.py +1403 -0
- alita_sdk/community/inventory/toolkit.py +173 -0
- alita_sdk/community/inventory/toolkit_utils.py +176 -0
- alita_sdk/community/inventory/visualize.py +1370 -0
- alita_sdk/configurations/__init__.py +1 -1
- alita_sdk/configurations/ado.py +141 -20
- alita_sdk/configurations/bitbucket.py +94 -2
- alita_sdk/configurations/confluence.py +130 -1
- alita_sdk/configurations/figma.py +76 -0
- alita_sdk/configurations/gitlab.py +91 -0
- alita_sdk/configurations/jira.py +103 -0
- alita_sdk/configurations/openapi.py +329 -0
- alita_sdk/configurations/qtest.py +72 -1
- alita_sdk/configurations/report_portal.py +96 -0
- alita_sdk/configurations/sharepoint.py +148 -0
- alita_sdk/configurations/testio.py +83 -0
- alita_sdk/configurations/testrail.py +88 -0
- alita_sdk/configurations/xray.py +93 -0
- alita_sdk/configurations/zephyr_enterprise.py +93 -0
- alita_sdk/configurations/zephyr_essential.py +75 -0
- alita_sdk/runtime/clients/artifact.py +3 -3
- alita_sdk/runtime/clients/client.py +388 -46
- alita_sdk/runtime/clients/mcp_discovery.py +342 -0
- alita_sdk/runtime/clients/mcp_manager.py +262 -0
- alita_sdk/runtime/clients/sandbox_client.py +8 -21
- alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
- alita_sdk/runtime/langchain/assistant.py +157 -39
- alita_sdk/runtime/langchain/constants.py +647 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
- alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -4
- alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +226 -7
- alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
- alita_sdk/runtime/langchain/document_loaders/constants.py +40 -19
- alita_sdk/runtime/langchain/langraph_agent.py +405 -84
- alita_sdk/runtime/langchain/utils.py +106 -7
- alita_sdk/runtime/llms/preloaded.py +2 -6
- alita_sdk/runtime/models/mcp_models.py +61 -0
- alita_sdk/runtime/skills/__init__.py +91 -0
- alita_sdk/runtime/skills/callbacks.py +498 -0
- alita_sdk/runtime/skills/discovery.py +540 -0
- alita_sdk/runtime/skills/executor.py +610 -0
- alita_sdk/runtime/skills/input_builder.py +371 -0
- alita_sdk/runtime/skills/models.py +330 -0
- alita_sdk/runtime/skills/registry.py +355 -0
- alita_sdk/runtime/skills/skill_runner.py +330 -0
- alita_sdk/runtime/toolkits/__init__.py +31 -0
- alita_sdk/runtime/toolkits/application.py +29 -10
- alita_sdk/runtime/toolkits/artifact.py +20 -11
- alita_sdk/runtime/toolkits/datasource.py +13 -6
- alita_sdk/runtime/toolkits/mcp.py +783 -0
- alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
- alita_sdk/runtime/toolkits/planning.py +178 -0
- alita_sdk/runtime/toolkits/skill_router.py +238 -0
- alita_sdk/runtime/toolkits/subgraph.py +251 -6
- alita_sdk/runtime/toolkits/tools.py +356 -69
- alita_sdk/runtime/toolkits/vectorstore.py +11 -5
- alita_sdk/runtime/tools/__init__.py +10 -3
- alita_sdk/runtime/tools/application.py +27 -6
- alita_sdk/runtime/tools/artifact.py +511 -28
- alita_sdk/runtime/tools/data_analysis.py +183 -0
- alita_sdk/runtime/tools/function.py +67 -35
- alita_sdk/runtime/tools/graph.py +10 -4
- alita_sdk/runtime/tools/image_generation.py +148 -46
- alita_sdk/runtime/tools/llm.py +1003 -128
- alita_sdk/runtime/tools/loop.py +3 -1
- alita_sdk/runtime/tools/loop_output.py +3 -1
- alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
- alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
- alita_sdk/runtime/tools/mcp_server_tool.py +8 -5
- alita_sdk/runtime/tools/planning/__init__.py +36 -0
- alita_sdk/runtime/tools/planning/models.py +246 -0
- alita_sdk/runtime/tools/planning/wrapper.py +607 -0
- alita_sdk/runtime/tools/router.py +2 -4
- alita_sdk/runtime/tools/sandbox.py +65 -48
- alita_sdk/runtime/tools/skill_router.py +776 -0
- alita_sdk/runtime/tools/tool.py +3 -1
- alita_sdk/runtime/tools/vectorstore.py +9 -3
- alita_sdk/runtime/tools/vectorstore_base.py +70 -14
- alita_sdk/runtime/utils/AlitaCallback.py +137 -21
- alita_sdk/runtime/utils/constants.py +5 -1
- alita_sdk/runtime/utils/mcp_client.py +492 -0
- alita_sdk/runtime/utils/mcp_oauth.py +361 -0
- alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
- alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
- alita_sdk/runtime/utils/serialization.py +155 -0
- alita_sdk/runtime/utils/streamlit.py +40 -13
- alita_sdk/runtime/utils/toolkit_utils.py +30 -9
- alita_sdk/runtime/utils/utils.py +36 -0
- alita_sdk/tools/__init__.py +134 -35
- alita_sdk/tools/ado/repos/__init__.py +51 -32
- alita_sdk/tools/ado/repos/repos_wrapper.py +148 -89
- alita_sdk/tools/ado/test_plan/__init__.py +25 -9
- alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
- alita_sdk/tools/ado/utils.py +1 -18
- alita_sdk/tools/ado/wiki/__init__.py +25 -12
- alita_sdk/tools/ado/wiki/ado_wrapper.py +291 -22
- alita_sdk/tools/ado/work_item/__init__.py +26 -13
- alita_sdk/tools/ado/work_item/ado_wrapper.py +73 -11
- alita_sdk/tools/advanced_jira_mining/__init__.py +11 -8
- alita_sdk/tools/aws/delta_lake/__init__.py +13 -9
- alita_sdk/tools/aws/delta_lake/tool.py +5 -1
- alita_sdk/tools/azure_ai/search/__init__.py +11 -8
- alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
- alita_sdk/tools/base/tool.py +5 -1
- alita_sdk/tools/base_indexer_toolkit.py +271 -84
- alita_sdk/tools/bitbucket/__init__.py +17 -11
- alita_sdk/tools/bitbucket/api_wrapper.py +59 -11
- alita_sdk/tools/bitbucket/cloud_api_wrapper.py +49 -35
- alita_sdk/tools/browser/__init__.py +5 -4
- alita_sdk/tools/carrier/__init__.py +5 -6
- alita_sdk/tools/carrier/backend_reports_tool.py +6 -6
- alita_sdk/tools/carrier/run_ui_test_tool.py +6 -6
- alita_sdk/tools/carrier/ui_reports_tool.py +5 -5
- alita_sdk/tools/chunkers/__init__.py +3 -1
- alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
- alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
- alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
- alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
- alita_sdk/tools/chunkers/universal_chunker.py +270 -0
- alita_sdk/tools/cloud/aws/__init__.py +10 -7
- alita_sdk/tools/cloud/azure/__init__.py +10 -7
- alita_sdk/tools/cloud/gcp/__init__.py +10 -7
- alita_sdk/tools/cloud/k8s/__init__.py +10 -7
- alita_sdk/tools/code/linter/__init__.py +10 -8
- alita_sdk/tools/code/loaders/codesearcher.py +3 -2
- alita_sdk/tools/code/sonar/__init__.py +11 -8
- alita_sdk/tools/code_indexer_toolkit.py +82 -22
- alita_sdk/tools/confluence/__init__.py +22 -16
- alita_sdk/tools/confluence/api_wrapper.py +107 -30
- alita_sdk/tools/confluence/loader.py +14 -2
- alita_sdk/tools/custom_open_api/__init__.py +12 -5
- alita_sdk/tools/elastic/__init__.py +11 -8
- alita_sdk/tools/elitea_base.py +493 -30
- alita_sdk/tools/figma/__init__.py +58 -11
- alita_sdk/tools/figma/api_wrapper.py +1235 -143
- alita_sdk/tools/figma/figma_client.py +73 -0
- alita_sdk/tools/figma/toon_tools.py +2748 -0
- alita_sdk/tools/github/__init__.py +14 -15
- alita_sdk/tools/github/github_client.py +224 -100
- alita_sdk/tools/github/graphql_client_wrapper.py +119 -33
- alita_sdk/tools/github/schemas.py +14 -5
- alita_sdk/tools/github/tool.py +5 -1
- alita_sdk/tools/github/tool_prompts.py +9 -22
- alita_sdk/tools/gitlab/__init__.py +16 -11
- alita_sdk/tools/gitlab/api_wrapper.py +218 -48
- alita_sdk/tools/gitlab_org/__init__.py +10 -9
- alita_sdk/tools/gitlab_org/api_wrapper.py +63 -64
- alita_sdk/tools/google/bigquery/__init__.py +13 -12
- alita_sdk/tools/google/bigquery/tool.py +5 -1
- alita_sdk/tools/google_places/__init__.py +11 -8
- alita_sdk/tools/google_places/api_wrapper.py +1 -1
- alita_sdk/tools/jira/__init__.py +17 -10
- alita_sdk/tools/jira/api_wrapper.py +92 -41
- alita_sdk/tools/keycloak/__init__.py +11 -8
- alita_sdk/tools/localgit/__init__.py +9 -3
- alita_sdk/tools/localgit/local_git.py +62 -54
- alita_sdk/tools/localgit/tool.py +5 -1
- alita_sdk/tools/memory/__init__.py +12 -4
- alita_sdk/tools/non_code_indexer_toolkit.py +1 -0
- alita_sdk/tools/ocr/__init__.py +11 -8
- alita_sdk/tools/openapi/__init__.py +491 -106
- alita_sdk/tools/openapi/api_wrapper.py +1368 -0
- alita_sdk/tools/openapi/tool.py +20 -0
- alita_sdk/tools/pandas/__init__.py +20 -12
- alita_sdk/tools/pandas/api_wrapper.py +38 -25
- alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
- alita_sdk/tools/postman/__init__.py +10 -9
- alita_sdk/tools/pptx/__init__.py +11 -10
- alita_sdk/tools/pptx/pptx_wrapper.py +1 -1
- alita_sdk/tools/qtest/__init__.py +31 -11
- alita_sdk/tools/qtest/api_wrapper.py +2135 -86
- alita_sdk/tools/rally/__init__.py +10 -9
- alita_sdk/tools/rally/api_wrapper.py +1 -1
- alita_sdk/tools/report_portal/__init__.py +12 -8
- alita_sdk/tools/salesforce/__init__.py +10 -8
- alita_sdk/tools/servicenow/__init__.py +17 -15
- alita_sdk/tools/servicenow/api_wrapper.py +1 -1
- alita_sdk/tools/sharepoint/__init__.py +10 -7
- alita_sdk/tools/sharepoint/api_wrapper.py +129 -38
- alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
- alita_sdk/tools/sharepoint/utils.py +8 -2
- alita_sdk/tools/slack/__init__.py +10 -7
- alita_sdk/tools/slack/api_wrapper.py +2 -2
- alita_sdk/tools/sql/__init__.py +12 -9
- alita_sdk/tools/testio/__init__.py +10 -7
- alita_sdk/tools/testrail/__init__.py +11 -10
- alita_sdk/tools/testrail/api_wrapper.py +1 -1
- alita_sdk/tools/utils/__init__.py +9 -4
- alita_sdk/tools/utils/content_parser.py +103 -18
- alita_sdk/tools/utils/text_operations.py +410 -0
- alita_sdk/tools/utils/tool_prompts.py +79 -0
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +30 -13
- alita_sdk/tools/xray/__init__.py +13 -9
- alita_sdk/tools/yagmail/__init__.py +9 -3
- alita_sdk/tools/zephyr/__init__.py +10 -7
- alita_sdk/tools/zephyr_enterprise/__init__.py +11 -7
- alita_sdk/tools/zephyr_essential/__init__.py +10 -7
- alita_sdk/tools/zephyr_essential/api_wrapper.py +30 -13
- alita_sdk/tools/zephyr_essential/client.py +2 -2
- alita_sdk/tools/zephyr_scale/__init__.py +11 -8
- alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
- alita_sdk/tools/zephyr_squad/__init__.py +10 -7
- {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/METADATA +154 -8
- alita_sdk-0.3.627.dist-info/RECORD +468 -0
- alita_sdk-0.3.627.dist-info/entry_points.txt +2 -0
- alita_sdk-0.3.379.dist-info/RECORD +0 -360
- {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Callback system for skill execution transparency.
|
|
3
|
+
|
|
4
|
+
This module provides a comprehensive callback system that allows real-time
|
|
5
|
+
monitoring of skill execution events, including tool usage, LLM calls,
|
|
6
|
+
and node transitions.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import threading
|
|
12
|
+
import time
|
|
13
|
+
from abc import ABC, abstractmethod
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, Dict, List, Optional
|
|
16
|
+
|
|
17
|
+
from langchain_core.callbacks import BaseCallbackHandler
|
|
18
|
+
from langchain_core.messages import BaseMessage
|
|
19
|
+
|
|
20
|
+
from .models import SkillEvent, SkillEventType
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SkillCallback(ABC):
|
|
26
|
+
"""
|
|
27
|
+
Abstract base class for skill execution callbacks.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def on_skill_event(
|
|
32
|
+
self,
|
|
33
|
+
event_type: SkillEventType,
|
|
34
|
+
data: Dict[str, Any],
|
|
35
|
+
skill_name: str,
|
|
36
|
+
execution_id: str
|
|
37
|
+
) -> None:
|
|
38
|
+
"""
|
|
39
|
+
Handle a skill execution event.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
event_type: Type of the event.
|
|
43
|
+
data: Event data dictionary.
|
|
44
|
+
skill_name: Name of the skill generating the event.
|
|
45
|
+
execution_id: Unique execution identifier.
|
|
46
|
+
"""
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class CallbackManager:
|
|
51
|
+
"""
|
|
52
|
+
Manager for multiple skill callbacks that forwards events to all registered callbacks.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, callbacks: Optional[List[SkillCallback]] = None):
|
|
56
|
+
"""
|
|
57
|
+
Initialize callback manager.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
callbacks: List of initial callbacks to register.
|
|
61
|
+
"""
|
|
62
|
+
self.callbacks = callbacks or []
|
|
63
|
+
self._lock = threading.Lock()
|
|
64
|
+
|
|
65
|
+
def add_callback(self, callback: SkillCallback) -> None:
|
|
66
|
+
"""
|
|
67
|
+
Add a callback to the manager.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
callback: Callback to add.
|
|
71
|
+
"""
|
|
72
|
+
with self._lock:
|
|
73
|
+
self.callbacks.append(callback)
|
|
74
|
+
|
|
75
|
+
def remove_callback(self, callback: SkillCallback) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Remove a callback from the manager.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
callback: Callback to remove.
|
|
81
|
+
"""
|
|
82
|
+
with self._lock:
|
|
83
|
+
if callback in self.callbacks:
|
|
84
|
+
self.callbacks.remove(callback)
|
|
85
|
+
|
|
86
|
+
def emit_event(
|
|
87
|
+
self,
|
|
88
|
+
event_type: SkillEventType,
|
|
89
|
+
data: Dict[str, Any],
|
|
90
|
+
skill_name: str,
|
|
91
|
+
execution_id: str
|
|
92
|
+
) -> None:
|
|
93
|
+
"""
|
|
94
|
+
Emit an event to all registered callbacks.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
event_type: Type of the event.
|
|
98
|
+
data: Event data dictionary.
|
|
99
|
+
skill_name: Name of the skill generating the event.
|
|
100
|
+
execution_id: Unique execution identifier.
|
|
101
|
+
"""
|
|
102
|
+
# Create event object
|
|
103
|
+
event = SkillEvent(
|
|
104
|
+
event_type=event_type,
|
|
105
|
+
data=data.copy(),
|
|
106
|
+
skill_name=skill_name,
|
|
107
|
+
execution_id=execution_id,
|
|
108
|
+
timestamp=time.time()
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Forward to all callbacks (thread-safe)
|
|
112
|
+
callbacks_snapshot = []
|
|
113
|
+
with self._lock:
|
|
114
|
+
callbacks_snapshot = self.callbacks.copy()
|
|
115
|
+
|
|
116
|
+
for callback in callbacks_snapshot:
|
|
117
|
+
try:
|
|
118
|
+
callback.on_skill_event(event_type, data, skill_name, execution_id)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
# Don't let callback errors break skill execution
|
|
121
|
+
logger.warning(
|
|
122
|
+
f"Callback {callback.__class__.__name__} failed for event "
|
|
123
|
+
f"{event_type.value}: {e}"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def clear(self) -> None:
|
|
127
|
+
"""Clear all registered callbacks."""
|
|
128
|
+
with self._lock:
|
|
129
|
+
self.callbacks.clear()
|
|
130
|
+
|
|
131
|
+
def __len__(self) -> int:
|
|
132
|
+
"""Return number of registered callbacks."""
|
|
133
|
+
with self._lock:
|
|
134
|
+
return len(self.callbacks)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class LoggingCallback(SkillCallback):
|
|
138
|
+
"""
|
|
139
|
+
Simple callback that logs all events.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
def __init__(self, level: int = logging.INFO):
|
|
143
|
+
"""
|
|
144
|
+
Initialize logging callback.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
level: Logging level to use for events.
|
|
148
|
+
"""
|
|
149
|
+
self.level = level
|
|
150
|
+
self.logger = logging.getLogger(f"{__name__}.LoggingCallback")
|
|
151
|
+
|
|
152
|
+
def on_skill_event(
|
|
153
|
+
self,
|
|
154
|
+
event_type: SkillEventType,
|
|
155
|
+
data: Dict[str, Any],
|
|
156
|
+
skill_name: str,
|
|
157
|
+
execution_id: str
|
|
158
|
+
) -> None:
|
|
159
|
+
"""Log the skill event."""
|
|
160
|
+
message = f"[{skill_name}:{execution_id[-8:]}] {event_type.value}"
|
|
161
|
+
if data:
|
|
162
|
+
message += f": {data}"
|
|
163
|
+
|
|
164
|
+
self.logger.log(self.level, message)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class ProgressCallback(SkillCallback):
|
|
168
|
+
"""
|
|
169
|
+
Callback that tracks and displays execution progress.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
def __init__(self):
|
|
173
|
+
"""Initialize progress callback."""
|
|
174
|
+
self.start_times: Dict[str, float] = {}
|
|
175
|
+
self.node_counts: Dict[str, int] = {}
|
|
176
|
+
|
|
177
|
+
def on_skill_event(
|
|
178
|
+
self,
|
|
179
|
+
event_type: SkillEventType,
|
|
180
|
+
data: Dict[str, Any],
|
|
181
|
+
skill_name: str,
|
|
182
|
+
execution_id: str
|
|
183
|
+
) -> None:
|
|
184
|
+
"""Track progress events."""
|
|
185
|
+
if event_type == SkillEventType.SKILL_START:
|
|
186
|
+
self.start_times[execution_id] = time.time()
|
|
187
|
+
self.node_counts[execution_id] = 0
|
|
188
|
+
print(f"🚀 Starting skill: {skill_name}")
|
|
189
|
+
|
|
190
|
+
elif event_type == SkillEventType.SKILL_END:
|
|
191
|
+
if execution_id in self.start_times:
|
|
192
|
+
duration = time.time() - self.start_times[execution_id]
|
|
193
|
+
nodes = self.node_counts.get(execution_id, 0)
|
|
194
|
+
print(f"✅ Completed skill: {skill_name} ({duration:.1f}s, {nodes} nodes)")
|
|
195
|
+
|
|
196
|
+
elif event_type == SkillEventType.NODE_START:
|
|
197
|
+
node_name = data.get('node', 'unknown')
|
|
198
|
+
print(f" 🔄 Executing node: {node_name}")
|
|
199
|
+
self.node_counts[execution_id] = self.node_counts.get(execution_id, 0) + 1
|
|
200
|
+
|
|
201
|
+
elif event_type == SkillEventType.TOOL_START:
|
|
202
|
+
tool_name = data.get('tool', 'unknown')
|
|
203
|
+
print(f" 🔧 Using tool: {tool_name}")
|
|
204
|
+
|
|
205
|
+
elif event_type == SkillEventType.LLM_START:
|
|
206
|
+
model = data.get('model', 'unknown')
|
|
207
|
+
print(f" 🤖 Calling LLM: {model}")
|
|
208
|
+
|
|
209
|
+
elif event_type == SkillEventType.ERROR:
|
|
210
|
+
error = data.get('error', 'unknown error')
|
|
211
|
+
print(f"❌ Error in skill {skill_name}: {error}")
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class FileCallback(SkillCallback):
|
|
215
|
+
"""
|
|
216
|
+
Callback that writes events to a file for analysis or debugging.
|
|
217
|
+
"""
|
|
218
|
+
|
|
219
|
+
def __init__(self, file_path: Path, json_format: bool = True):
|
|
220
|
+
"""
|
|
221
|
+
Initialize file callback.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
file_path: Path to write events to.
|
|
225
|
+
json_format: Whether to write events as JSON (True) or text (False).
|
|
226
|
+
"""
|
|
227
|
+
self.file_path = file_path
|
|
228
|
+
self.json_format = json_format
|
|
229
|
+
self._lock = threading.Lock()
|
|
230
|
+
|
|
231
|
+
# Ensure directory exists
|
|
232
|
+
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
233
|
+
|
|
234
|
+
def on_skill_event(
|
|
235
|
+
self,
|
|
236
|
+
event_type: SkillEventType,
|
|
237
|
+
data: Dict[str, Any],
|
|
238
|
+
skill_name: str,
|
|
239
|
+
execution_id: str
|
|
240
|
+
) -> None:
|
|
241
|
+
"""Write event to file."""
|
|
242
|
+
event = SkillEvent(
|
|
243
|
+
event_type=event_type,
|
|
244
|
+
data=data,
|
|
245
|
+
skill_name=skill_name,
|
|
246
|
+
execution_id=execution_id,
|
|
247
|
+
timestamp=time.time()
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
with self._lock:
|
|
251
|
+
try:
|
|
252
|
+
with open(self.file_path, 'a', encoding='utf-8') as f:
|
|
253
|
+
if self.json_format:
|
|
254
|
+
f.write(json.dumps(event.to_dict()) + '\n')
|
|
255
|
+
else:
|
|
256
|
+
f.write(f"{event.timestamp} [{skill_name}:{execution_id}] "
|
|
257
|
+
f"{event_type.value}: {data}\n")
|
|
258
|
+
except Exception as e:
|
|
259
|
+
logger.warning(f"Failed to write event to file {self.file_path}: {e}")
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class SkillLangChainCallback(BaseCallbackHandler):
|
|
263
|
+
"""
|
|
264
|
+
LangChain callback handler that forwards events to the skill callback system.
|
|
265
|
+
"""
|
|
266
|
+
|
|
267
|
+
def __init__(self, callback_manager: CallbackManager, skill_name: str, execution_id: str):
|
|
268
|
+
"""
|
|
269
|
+
Initialize LangChain callback handler.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
callback_manager: Callback manager to forward events to.
|
|
273
|
+
skill_name: Name of the skill being executed.
|
|
274
|
+
execution_id: Execution identifier.
|
|
275
|
+
"""
|
|
276
|
+
super().__init__()
|
|
277
|
+
self.callback_manager = callback_manager
|
|
278
|
+
self.skill_name = skill_name
|
|
279
|
+
self.execution_id = execution_id
|
|
280
|
+
|
|
281
|
+
def on_tool_start(
|
|
282
|
+
self,
|
|
283
|
+
serialized: Dict[str, Any],
|
|
284
|
+
input_str: str,
|
|
285
|
+
**kwargs: Any
|
|
286
|
+
) -> None:
|
|
287
|
+
"""Handle tool start event."""
|
|
288
|
+
self.callback_manager.emit_event(
|
|
289
|
+
SkillEventType.TOOL_START,
|
|
290
|
+
{
|
|
291
|
+
"tool": serialized.get("name", "unknown"),
|
|
292
|
+
"input": input_str,
|
|
293
|
+
"serialized": serialized
|
|
294
|
+
},
|
|
295
|
+
self.skill_name,
|
|
296
|
+
self.execution_id
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
def on_tool_end(
|
|
300
|
+
self,
|
|
301
|
+
output: str,
|
|
302
|
+
**kwargs: Any
|
|
303
|
+
) -> None:
|
|
304
|
+
"""Handle tool end event."""
|
|
305
|
+
self.callback_manager.emit_event(
|
|
306
|
+
SkillEventType.TOOL_END,
|
|
307
|
+
{
|
|
308
|
+
"output": output
|
|
309
|
+
},
|
|
310
|
+
self.skill_name,
|
|
311
|
+
self.execution_id
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
def on_tool_error(
|
|
315
|
+
self,
|
|
316
|
+
error: Exception,
|
|
317
|
+
**kwargs: Any
|
|
318
|
+
) -> None:
|
|
319
|
+
"""Handle tool error event."""
|
|
320
|
+
self.callback_manager.emit_event(
|
|
321
|
+
SkillEventType.ERROR,
|
|
322
|
+
{
|
|
323
|
+
"error": str(error),
|
|
324
|
+
"error_type": "tool_error"
|
|
325
|
+
},
|
|
326
|
+
self.skill_name,
|
|
327
|
+
self.execution_id
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
def on_llm_start(
|
|
331
|
+
self,
|
|
332
|
+
serialized: Dict[str, Any],
|
|
333
|
+
prompts: List[str],
|
|
334
|
+
**kwargs: Any
|
|
335
|
+
) -> None:
|
|
336
|
+
"""Handle LLM start event."""
|
|
337
|
+
self.callback_manager.emit_event(
|
|
338
|
+
SkillEventType.LLM_START,
|
|
339
|
+
{
|
|
340
|
+
"model": serialized.get("model_name", "unknown"),
|
|
341
|
+
"prompts": prompts,
|
|
342
|
+
"serialized": serialized
|
|
343
|
+
},
|
|
344
|
+
self.skill_name,
|
|
345
|
+
self.execution_id
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
def on_llm_end(
|
|
349
|
+
self,
|
|
350
|
+
response: Any,
|
|
351
|
+
**kwargs: Any
|
|
352
|
+
) -> None:
|
|
353
|
+
"""Handle LLM end event."""
|
|
354
|
+
self.callback_manager.emit_event(
|
|
355
|
+
SkillEventType.LLM_END,
|
|
356
|
+
{
|
|
357
|
+
"response": str(response)
|
|
358
|
+
},
|
|
359
|
+
self.skill_name,
|
|
360
|
+
self.execution_id
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
def on_llm_error(
|
|
364
|
+
self,
|
|
365
|
+
error: Exception,
|
|
366
|
+
**kwargs: Any
|
|
367
|
+
) -> None:
|
|
368
|
+
"""Handle LLM error event."""
|
|
369
|
+
self.callback_manager.emit_event(
|
|
370
|
+
SkillEventType.ERROR,
|
|
371
|
+
{
|
|
372
|
+
"error": str(error),
|
|
373
|
+
"error_type": "llm_error"
|
|
374
|
+
},
|
|
375
|
+
self.skill_name,
|
|
376
|
+
self.execution_id
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
def on_chain_start(
|
|
380
|
+
self,
|
|
381
|
+
serialized: Dict[str, Any],
|
|
382
|
+
inputs: Dict[str, Any],
|
|
383
|
+
**kwargs: Any
|
|
384
|
+
) -> None:
|
|
385
|
+
"""Handle chain start event."""
|
|
386
|
+
self.callback_manager.emit_event(
|
|
387
|
+
SkillEventType.CUSTOM_EVENT,
|
|
388
|
+
{
|
|
389
|
+
"event": "chain_start",
|
|
390
|
+
"chain": serialized.get("name", "unknown"),
|
|
391
|
+
"inputs": inputs
|
|
392
|
+
},
|
|
393
|
+
self.skill_name,
|
|
394
|
+
self.execution_id
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
def on_chain_end(
|
|
398
|
+
self,
|
|
399
|
+
outputs: Dict[str, Any],
|
|
400
|
+
**kwargs: Any
|
|
401
|
+
) -> None:
|
|
402
|
+
"""Handle chain end event."""
|
|
403
|
+
self.callback_manager.emit_event(
|
|
404
|
+
SkillEventType.CUSTOM_EVENT,
|
|
405
|
+
{
|
|
406
|
+
"event": "chain_end",
|
|
407
|
+
"outputs": outputs
|
|
408
|
+
},
|
|
409
|
+
self.skill_name,
|
|
410
|
+
self.execution_id
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
class CallbackEmitter:
|
|
415
|
+
"""
|
|
416
|
+
Helper class for emitting callbacks from subprocess execution.
|
|
417
|
+
|
|
418
|
+
This class writes events to a pipe file that can be monitored by the parent process.
|
|
419
|
+
"""
|
|
420
|
+
|
|
421
|
+
def __init__(self, pipe_path: Optional[str], execution_id: str):
|
|
422
|
+
"""
|
|
423
|
+
Initialize callback emitter.
|
|
424
|
+
|
|
425
|
+
Args:
|
|
426
|
+
pipe_path: Path to callback pipe file.
|
|
427
|
+
execution_id: Execution identifier.
|
|
428
|
+
"""
|
|
429
|
+
self.pipe_path = Path(pipe_path) if pipe_path else None
|
|
430
|
+
self.execution_id = execution_id
|
|
431
|
+
self._lock = threading.Lock()
|
|
432
|
+
|
|
433
|
+
def emit(
|
|
434
|
+
self,
|
|
435
|
+
event_type: SkillEventType,
|
|
436
|
+
data: Dict[str, Any],
|
|
437
|
+
skill_name: str
|
|
438
|
+
) -> None:
|
|
439
|
+
"""
|
|
440
|
+
Emit an event to the pipe file.
|
|
441
|
+
|
|
442
|
+
Args:
|
|
443
|
+
event_type: Type of event.
|
|
444
|
+
data: Event data.
|
|
445
|
+
skill_name: Name of skill generating event.
|
|
446
|
+
"""
|
|
447
|
+
if not self.pipe_path:
|
|
448
|
+
return
|
|
449
|
+
|
|
450
|
+
event = SkillEvent(
|
|
451
|
+
event_type=event_type,
|
|
452
|
+
data=data,
|
|
453
|
+
skill_name=skill_name,
|
|
454
|
+
execution_id=self.execution_id,
|
|
455
|
+
timestamp=time.time()
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
with self._lock:
|
|
459
|
+
try:
|
|
460
|
+
with open(self.pipe_path, 'a', encoding='utf-8') as f:
|
|
461
|
+
f.write(json.dumps(event.to_dict()) + '\n')
|
|
462
|
+
f.flush()
|
|
463
|
+
except Exception as e:
|
|
464
|
+
logger.warning(f"Failed to emit callback event: {e}")
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def create_default_callbacks() -> List[SkillCallback]:
|
|
468
|
+
"""
|
|
469
|
+
Create a default set of callbacks for skill execution.
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
List of default callback instances.
|
|
473
|
+
"""
|
|
474
|
+
return [
|
|
475
|
+
LoggingCallback(level=logging.INFO),
|
|
476
|
+
ProgressCallback()
|
|
477
|
+
]
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def create_debug_callbacks(log_file: Optional[Path] = None) -> List[SkillCallback]:
|
|
481
|
+
"""
|
|
482
|
+
Create callbacks suitable for debugging skill execution.
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
log_file: Optional path to write detailed event log.
|
|
486
|
+
|
|
487
|
+
Returns:
|
|
488
|
+
List of debug callback instances.
|
|
489
|
+
"""
|
|
490
|
+
callbacks = [
|
|
491
|
+
LoggingCallback(level=logging.DEBUG),
|
|
492
|
+
ProgressCallback()
|
|
493
|
+
]
|
|
494
|
+
|
|
495
|
+
if log_file:
|
|
496
|
+
callbacks.append(FileCallback(log_file, json_format=True))
|
|
497
|
+
|
|
498
|
+
return callbacks
|