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,246 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SQLAlchemy models for agent planning.
|
|
3
|
+
|
|
4
|
+
Defines the AgentPlan table for storing execution plans with steps.
|
|
5
|
+
Table is created automatically on toolkit initialization if it doesn't exist.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import enum
|
|
9
|
+
import logging
|
|
10
|
+
import uuid
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from typing import List, Dict, Any, Optional
|
|
13
|
+
|
|
14
|
+
from pydantic import BaseModel, Field
|
|
15
|
+
from sqlalchemy import Column, String, DateTime, Text, Index, text
|
|
16
|
+
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
|
17
|
+
from sqlalchemy.orm import declarative_base
|
|
18
|
+
from sqlalchemy import create_engine
|
|
19
|
+
from sqlalchemy.exc import ProgrammingError
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
Base = declarative_base()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PlanStatus(str, enum.Enum):
|
|
27
|
+
"""Status of an execution plan."""
|
|
28
|
+
in_progress = "in_progress"
|
|
29
|
+
completed = "completed"
|
|
30
|
+
abandoned = "abandoned"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class AgentPlan(Base):
|
|
34
|
+
"""
|
|
35
|
+
Stores execution plans for agent tasks.
|
|
36
|
+
|
|
37
|
+
Created in the project-specific pgvector database.
|
|
38
|
+
Plans are scoped by conversation_id (from server or CLI session_id).
|
|
39
|
+
"""
|
|
40
|
+
__tablename__ = "agent_plans"
|
|
41
|
+
|
|
42
|
+
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
43
|
+
conversation_id = Column(String(255), nullable=False, index=True)
|
|
44
|
+
|
|
45
|
+
# Plan metadata
|
|
46
|
+
title = Column(String(255), nullable=True)
|
|
47
|
+
status = Column(String(50), default=PlanStatus.in_progress.value)
|
|
48
|
+
|
|
49
|
+
# Plan content (JSONB for flexible step storage)
|
|
50
|
+
# Structure: {"steps": [{"description": "...", "completed": false}, ...]}
|
|
51
|
+
plan_data = Column(JSONB, nullable=False, default=dict)
|
|
52
|
+
|
|
53
|
+
# Timestamps
|
|
54
|
+
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
|
|
55
|
+
updated_at = Column(DateTime, nullable=True, onupdate=datetime.utcnow)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Pydantic models for tool input/output
|
|
59
|
+
class PlanStep(BaseModel):
|
|
60
|
+
"""A single step in a plan."""
|
|
61
|
+
description: str = Field(description="Step description")
|
|
62
|
+
completed: bool = Field(default=False, description="Whether step is completed")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class PlanState(BaseModel):
|
|
66
|
+
"""Current plan state for serialization."""
|
|
67
|
+
title: str = Field(default="", description="Plan title")
|
|
68
|
+
steps: List[PlanStep] = Field(default_factory=list, description="List of steps")
|
|
69
|
+
status: str = Field(default=PlanStatus.in_progress.value, description="Plan status")
|
|
70
|
+
|
|
71
|
+
def render(self) -> str:
|
|
72
|
+
"""Render plan as formatted string with checkboxes."""
|
|
73
|
+
if not self.steps:
|
|
74
|
+
return "No plan currently set."
|
|
75
|
+
|
|
76
|
+
lines = []
|
|
77
|
+
if self.title:
|
|
78
|
+
lines.append(f"📋 {self.title}")
|
|
79
|
+
|
|
80
|
+
completed_count = 0
|
|
81
|
+
for i, step in enumerate(self.steps, 1):
|
|
82
|
+
checkbox = "☑" if step.completed else "☐"
|
|
83
|
+
status_text = " (completed)" if step.completed else ""
|
|
84
|
+
lines.append(f" {checkbox} {i}. {step.description}{status_text}")
|
|
85
|
+
if step.completed:
|
|
86
|
+
completed_count += 1
|
|
87
|
+
|
|
88
|
+
lines.append(f"\nProgress: {completed_count}/{len(self.steps)} steps completed")
|
|
89
|
+
|
|
90
|
+
return "\n".join(lines)
|
|
91
|
+
|
|
92
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
93
|
+
"""Convert to dictionary for JSONB storage."""
|
|
94
|
+
return {
|
|
95
|
+
"steps": [{"description": s.description, "completed": s.completed} for s in self.steps]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def from_dict(cls, data: Dict[str, Any], title: str = "", status: str = PlanStatus.in_progress.value) -> "PlanState":
|
|
100
|
+
"""Create from dictionary (JSONB data)."""
|
|
101
|
+
steps_data = data.get("steps", [])
|
|
102
|
+
steps = [PlanStep(**s) if isinstance(s, dict) else s for s in steps_data]
|
|
103
|
+
return cls(title=title, steps=steps, status=status)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def ensure_plan_tables(connection_string: str) -> bool:
|
|
107
|
+
"""
|
|
108
|
+
Ensure the agent_plans table exists in the database.
|
|
109
|
+
|
|
110
|
+
Creates the table if it doesn't exist. Safe to call multiple times.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
connection_string: PostgreSQL connection string
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
True if table was created or already exists, False on error
|
|
117
|
+
"""
|
|
118
|
+
try:
|
|
119
|
+
# Handle SecretStr if passed
|
|
120
|
+
if hasattr(connection_string, 'get_secret_value'):
|
|
121
|
+
connection_string = connection_string.get_secret_value()
|
|
122
|
+
|
|
123
|
+
if not connection_string:
|
|
124
|
+
logger.warning("No connection string provided for plan tables")
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
engine = create_engine(connection_string)
|
|
128
|
+
|
|
129
|
+
# Create tables if they don't exist
|
|
130
|
+
Base.metadata.create_all(engine, checkfirst=True)
|
|
131
|
+
|
|
132
|
+
logger.debug("Agent plans table ensured")
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
except Exception as e:
|
|
136
|
+
logger.error(f"Failed to ensure plan tables: {e}")
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def delete_plan_by_conversation_id(connection_string: str, conversation_id: str) -> bool:
|
|
141
|
+
"""
|
|
142
|
+
Delete a plan by conversation_id.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
connection_string: PostgreSQL connection string
|
|
146
|
+
conversation_id: The conversation ID to delete plans for
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
True if deletion successful, False otherwise
|
|
150
|
+
"""
|
|
151
|
+
try:
|
|
152
|
+
if hasattr(connection_string, 'get_secret_value'):
|
|
153
|
+
connection_string = connection_string.get_secret_value()
|
|
154
|
+
|
|
155
|
+
if not connection_string or not conversation_id:
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
engine = create_engine(connection_string)
|
|
159
|
+
|
|
160
|
+
with engine.connect() as conn:
|
|
161
|
+
result = conn.execute(
|
|
162
|
+
text("DELETE FROM agent_plans WHERE conversation_id = :conversation_id"),
|
|
163
|
+
{"conversation_id": conversation_id}
|
|
164
|
+
)
|
|
165
|
+
conn.commit()
|
|
166
|
+
|
|
167
|
+
logger.debug(f"Deleted plan for conversation_id: {conversation_id}")
|
|
168
|
+
return True
|
|
169
|
+
|
|
170
|
+
except Exception as e:
|
|
171
|
+
logger.error(f"Failed to delete plan for conversation_id {conversation_id}: {e}")
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def cleanup_on_graceful_completion(
|
|
176
|
+
connection_string: str,
|
|
177
|
+
conversation_id: str,
|
|
178
|
+
thread_id: str = None,
|
|
179
|
+
delete_checkpoints: bool = True
|
|
180
|
+
) -> dict:
|
|
181
|
+
"""
|
|
182
|
+
Cleanup plans and optionally checkpoints after graceful agent completion.
|
|
183
|
+
|
|
184
|
+
This function is designed to be called after an agent completes successfully
|
|
185
|
+
(no exceptions, valid finish reason).
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
connection_string: PostgreSQL connection string
|
|
189
|
+
conversation_id: The conversation ID to cleanup plans for
|
|
190
|
+
thread_id: The thread ID to cleanup checkpoints for (optional)
|
|
191
|
+
delete_checkpoints: If True, also delete checkpoint data
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Dict with cleanup results: {'plan_deleted': bool, 'checkpoints_deleted': bool}
|
|
195
|
+
"""
|
|
196
|
+
result = {'plan_deleted': False, 'checkpoints_deleted': False}
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
if hasattr(connection_string, 'get_secret_value'):
|
|
200
|
+
connection_string = connection_string.get_secret_value()
|
|
201
|
+
|
|
202
|
+
if not connection_string or not conversation_id:
|
|
203
|
+
logger.warning("Missing connection_string or conversation_id for cleanup")
|
|
204
|
+
return result
|
|
205
|
+
|
|
206
|
+
engine = create_engine(connection_string)
|
|
207
|
+
|
|
208
|
+
with engine.connect() as conn:
|
|
209
|
+
# Delete plan by conversation_id
|
|
210
|
+
try:
|
|
211
|
+
conn.execute(
|
|
212
|
+
text("DELETE FROM agent_plans WHERE conversation_id = :conversation_id"),
|
|
213
|
+
{"conversation_id": conversation_id}
|
|
214
|
+
)
|
|
215
|
+
result['plan_deleted'] = True
|
|
216
|
+
logger.debug(f"Deleted plan for conversation_id: {conversation_id}")
|
|
217
|
+
except Exception as e:
|
|
218
|
+
# Table might not exist, which is fine
|
|
219
|
+
logger.debug(f"Could not delete plan (table may not exist): {e}")
|
|
220
|
+
|
|
221
|
+
# Delete checkpoints if requested (still uses thread_id as that's LangGraph's key)
|
|
222
|
+
if delete_checkpoints and thread_id:
|
|
223
|
+
checkpoint_tables = [
|
|
224
|
+
"checkpoints",
|
|
225
|
+
"checkpoint_writes",
|
|
226
|
+
"checkpoint_blobs"
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
for table in checkpoint_tables:
|
|
230
|
+
try:
|
|
231
|
+
conn.execute(
|
|
232
|
+
text(f"DELETE FROM {table} WHERE thread_id = :thread_id"),
|
|
233
|
+
{"thread_id": thread_id}
|
|
234
|
+
)
|
|
235
|
+
logger.debug(f"Deleted {table} for thread_id: {thread_id}")
|
|
236
|
+
except Exception as e:
|
|
237
|
+
logger.debug(f"Could not delete from {table}: {e}")
|
|
238
|
+
|
|
239
|
+
result['checkpoints_deleted'] = True
|
|
240
|
+
|
|
241
|
+
conn.commit()
|
|
242
|
+
|
|
243
|
+
except Exception as e:
|
|
244
|
+
logger.error(f"Failed to cleanup for conversation_id {conversation_id}: {e}")
|
|
245
|
+
|
|
246
|
+
return result
|