provision-stack-langchain 0.1.0__tar.gz

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.
@@ -0,0 +1,34 @@
1
+ node_modules
2
+ dist
3
+ build
4
+ coverage
5
+
6
+ .env
7
+ .env.*
8
+ !.env.example
9
+
10
+ .turbo
11
+
12
+ .DS_Store
13
+
14
+ .vscode/settings.json
15
+
16
+ *.log
17
+
18
+ # Generated Terraform deployment artifacts (written to user's cwd, not repo)
19
+ terraform/
20
+
21
+ # Local SQLite state (DATABASE_PATH default location)
22
+ data/
23
+
24
+ # Local deploy secrets (NEVER commit — deploy-time env lives here)
25
+ .env.local
26
+ deploy/.env
27
+
28
+ # Host SSH keys + terraform state (local artifacts)
29
+ deploy/hosting/provision-stack-host-key
30
+ deploy/hosting/provision-stack-host-key.pub
31
+ deploy/hosting/.terraform/
32
+ deploy/hosting/terraform.tfstate*
33
+ deploy/hosting/.terraform.lock.hcl
34
+ __pycache__/
@@ -0,0 +1,78 @@
1
+ Metadata-Version: 2.4
2
+ Name: provision-stack-langchain
3
+ Version: 0.1.0
4
+ Summary: LangChain tools for Provision Stack — deploy cloud infrastructure via AI agents.
5
+ Project-URL: Homepage, https://provision-stack.com
6
+ Project-URL: Documentation, https://docs.provision-stack.com
7
+ Project-URL: Repository, https://github.com/TravisLinkey/provision-stack
8
+ Author: Provision Stack
9
+ License-Expression: MIT
10
+ Keywords: ai-agents,aws,gcp,infrastructure,langchain,mcp
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: langchain-core>=0.3.0
22
+ Requires-Dist: provision-stack>=0.1.0
23
+ Provides-Extra: test
24
+ Requires-Dist: pytest-mock>=3.12; extra == 'test'
25
+ Requires-Dist: pytest>=8.0; extra == 'test'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # provision-stack-langchain
29
+
30
+ > LangChain tools for Provision Stack — deploy cloud infrastructure via AI agents.
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pip install provision-stack-langchain
36
+ ```
37
+
38
+ ## Quick Start
39
+
40
+ ```python
41
+ from provision_stack_langchain import get_provision_stack_tools
42
+
43
+ tools = get_provision_stack_tools(api_key="your-token", sandbox=True)
44
+
45
+ # Use with any LangChain agent
46
+ from langchain_openai import ChatOpenAI
47
+ from langchain.agents import initialize_agent, AgentType
48
+
49
+ llm = ChatOpenAI(model="gpt-4o")
50
+ agent = initialize_agent(tools, llm, agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION)
51
+
52
+ result = agent.invoke("Deploy a production web application on AWS under $50/month")
53
+ ```
54
+
55
+ ## Tools
56
+
57
+ | Tool | Description |
58
+ |------|-------------|
59
+ | `provision_stack_suggest` | Get deployment suggestions with tier options and costs |
60
+ | `provision_stack_deploy` | Deploy infrastructure (fee held, captured after verification) |
61
+ | `provision_stack_status` | Check deployment status and verification results |
62
+ | `provision_stack_destroy` | Destroy all resources owned by a deployment |
63
+ | `provision_stack_balance` | Check account credit balance |
64
+ | `provision_stack_topup` | Add credits via Stripe or XRP |
65
+
66
+ ## Configuration
67
+
68
+ ```python
69
+ tools = get_provision_stack_tools(
70
+ api_key="your-token", # or PROVISION_STACK_API_KEY env var
71
+ base_url="https://...", # or PROVISION_STACK_API_URL env var
72
+ sandbox=True, # use sandbox (mock Terraform)
73
+ )
74
+ ```
75
+
76
+ ## License
77
+
78
+ MIT
@@ -0,0 +1,51 @@
1
+ # provision-stack-langchain
2
+
3
+ > LangChain tools for Provision Stack — deploy cloud infrastructure via AI agents.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install provision-stack-langchain
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from provision_stack_langchain import get_provision_stack_tools
15
+
16
+ tools = get_provision_stack_tools(api_key="your-token", sandbox=True)
17
+
18
+ # Use with any LangChain agent
19
+ from langchain_openai import ChatOpenAI
20
+ from langchain.agents import initialize_agent, AgentType
21
+
22
+ llm = ChatOpenAI(model="gpt-4o")
23
+ agent = initialize_agent(tools, llm, agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION)
24
+
25
+ result = agent.invoke("Deploy a production web application on AWS under $50/month")
26
+ ```
27
+
28
+ ## Tools
29
+
30
+ | Tool | Description |
31
+ |------|-------------|
32
+ | `provision_stack_suggest` | Get deployment suggestions with tier options and costs |
33
+ | `provision_stack_deploy` | Deploy infrastructure (fee held, captured after verification) |
34
+ | `provision_stack_status` | Check deployment status and verification results |
35
+ | `provision_stack_destroy` | Destroy all resources owned by a deployment |
36
+ | `provision_stack_balance` | Check account credit balance |
37
+ | `provision_stack_topup` | Add credits via Stripe or XRP |
38
+
39
+ ## Configuration
40
+
41
+ ```python
42
+ tools = get_provision_stack_tools(
43
+ api_key="your-token", # or PROVISION_STACK_API_KEY env var
44
+ base_url="https://...", # or PROVISION_STACK_API_URL env var
45
+ sandbox=True, # use sandbox (mock Terraform)
46
+ )
47
+ ```
48
+
49
+ ## License
50
+
51
+ MIT
@@ -0,0 +1,14 @@
1
+ """
2
+ Provision Stack tools for LangChain.
3
+
4
+ Usage::
5
+
6
+ from provision_stack_langchain import get_provision_stack_tools
7
+
8
+ tools = get_provision_stack_tools(api_key="your-token")
9
+ agent = initialize_agent(tools, llm, agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION)
10
+ """
11
+
12
+ from provision_stack_langchain.tools import get_provision_stack_tools
13
+
14
+ __all__ = ["get_provision_stack_tools"]
@@ -0,0 +1,241 @@
1
+ """
2
+ LangChain tool wrappers for Provision Stack.
3
+
4
+ Each tool is a LangChain ``Tool`` instance that wraps the
5
+ corresponding Provision Stack API endpoint.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, Optional, Type
11
+
12
+ from langchain_core.tools import BaseTool
13
+ from pydantic import BaseModel, Field
14
+
15
+ from provision_stack.client import ProvisionStackClient
16
+ from provision_stack.config import PRODUCT_NAME
17
+
18
+
19
+ # ── Pydantic schemas for tool inputs ──────────────────────────────
20
+
21
+
22
+ class SuggestInput(BaseModel):
23
+ """Input for getting deployment suggestions."""
24
+
25
+ outcome: str = Field(
26
+ ...,
27
+ description="Natural language description of desired infrastructure "
28
+ "(e.g. 'deploy a production web application').",
29
+ )
30
+ region: Optional[str] = Field(
31
+ None,
32
+ description="Cloud region (e.g. 'us-east-1', 'europe-west1').",
33
+ )
34
+ provider: Optional[str] = Field(
35
+ None,
36
+ description="Restrict to one provider: 'aws', 'gcp', 'azure', 'oracle'.",
37
+ )
38
+ max_monthly_cost_usd: Optional[float] = Field(
39
+ None,
40
+ description="Monthly cost cap in USD. Options above this are filtered out.",
41
+ )
42
+
43
+
44
+ class DeployInput(BaseModel):
45
+ """Input for deploying infrastructure."""
46
+
47
+ outcome: str = Field(
48
+ ...,
49
+ description="Natural language description of what to deploy.",
50
+ )
51
+ tier: Optional[str] = Field(
52
+ None,
53
+ description="Deployment tier: 'Starter', 'MVP', 'Startup', 'Enterprise'.",
54
+ )
55
+ region: Optional[str] = Field(
56
+ None,
57
+ description="Target cloud region.",
58
+ )
59
+ max_monthly_cost_usd: Optional[float] = Field(
60
+ None,
61
+ description="Monthly cost cap in USD.",
62
+ )
63
+
64
+
65
+ class StatusInput(BaseModel):
66
+ """Input for checking deployment status."""
67
+
68
+ deployment_id: str = Field(
69
+ ...,
70
+ description="The deployment ID to check (e.g. 'd-1234567890-abc123').",
71
+ )
72
+
73
+
74
+ class DestroyInput(BaseModel):
75
+ """Input for destroying a deployment."""
76
+
77
+ deployment_id: str = Field(
78
+ ...,
79
+ description="The deployment ID to destroy.",
80
+ )
81
+
82
+
83
+ class TopupInput(BaseModel):
84
+ """Input for adding credits."""
85
+
86
+ amount_usd: float = Field(
87
+ ...,
88
+ description="Amount to add in USD.",
89
+ )
90
+ rail: str = Field(
91
+ "stripe",
92
+ description="Payment rail: 'stripe' (card) or 'xrp' (crypto).",
93
+ )
94
+
95
+
96
+ # ── LangChain tools ───────────────────────────────────────────────
97
+
98
+
99
+ class ProvisionStackSuggestTool(BaseTool):
100
+ """Get deployment suggestions for a desired outcome."""
101
+
102
+ name: str = "provision_stack_suggest"
103
+ description: str = (
104
+ f"Get deployment suggestions from {PRODUCT_NAME}. "
105
+ "Returns tier options (Starter/MVP/Startup/Enterprise) with "
106
+ "monthly cost estimates across multiple cloud providers."
107
+ )
108
+ args_schema: Type[BaseModel] = SuggestInput
109
+ client: ProvisionStackClient
110
+
111
+ def _run(
112
+ self,
113
+ outcome: str,
114
+ region: str | None = None,
115
+ provider: str | None = None,
116
+ max_monthly_cost_usd: float | None = None,
117
+ ) -> dict[str, Any]:
118
+ return self.client.suggest(
119
+ outcome=outcome,
120
+ region=region,
121
+ provider=provider,
122
+ max_monthly_cost_usd=max_monthly_cost_usd,
123
+ )
124
+
125
+
126
+ class ProvisionStackDeployTool(BaseTool):
127
+ """Deploy infrastructure for a selected suggestion."""
128
+
129
+ name: str = "provision_stack_deploy"
130
+ description: str = (
131
+ f"Deploy infrastructure via {PRODUCT_NAME}. "
132
+ "The deploy fee is held from your balance and captured only "
133
+ "after verification passes (failed deployments are free)."
134
+ )
135
+ args_schema: Type[BaseModel] = DeployInput
136
+ client: ProvisionStackClient
137
+
138
+ def _run(
139
+ self,
140
+ outcome: str,
141
+ tier: str | None = None,
142
+ region: str | None = None,
143
+ max_monthly_cost_usd: float | None = None,
144
+ ) -> dict[str, Any]:
145
+ return self.client.deploy(
146
+ outcome=outcome,
147
+ tier=tier,
148
+ region=region,
149
+ max_monthly_cost_usd=max_monthly_cost_usd,
150
+ )
151
+
152
+
153
+ class ProvisionStackStatusTool(BaseTool):
154
+ """Check deployment status and verification results."""
155
+
156
+ name: str = "provision_stack_status"
157
+ description: str = (
158
+ f"Check the status and verification results of a {PRODUCT_NAME} deployment."
159
+ )
160
+ args_schema: Type[BaseModel] = StatusInput
161
+ client: ProvisionStackClient
162
+
163
+ def _run(self, deployment_id: str) -> dict[str, Any]:
164
+ return self.client.status(deployment_id)
165
+
166
+
167
+ class ProvisionStackDestroyTool(BaseTool):
168
+ """Destroy all resources owned by a deployment."""
169
+
170
+ name: str = "provision_stack_destroy"
171
+ description: str = (
172
+ f"Destroy all cloud resources owned by a {PRODUCT_NAME} deployment."
173
+ )
174
+ args_schema: Type[BaseModel] = DestroyInput
175
+ client: ProvisionStackClient
176
+
177
+ def _run(self, deployment_id: str) -> dict[str, Any]:
178
+ return self.client.destroy(deployment_id)
179
+
180
+
181
+ class ProvisionStackBalanceTool(BaseTool):
182
+ """Check account credit balance."""
183
+
184
+ name: str = "provision_stack_balance"
185
+ description: str = f"Check your {PRODUCT_NAME} account credit balance."
186
+ client: ProvisionStackClient
187
+
188
+ def _run(self) -> dict[str, Any]:
189
+ return self.client.balance()
190
+
191
+
192
+ class ProvisionStackTopupTool(BaseTool):
193
+ """Add credits to your account."""
194
+
195
+ name: str = "provision_stack_topup"
196
+ description: str = (
197
+ f"Add credits to your {PRODUCT_NAME} account via Stripe (card) or XRP (crypto)."
198
+ )
199
+ args_schema: Type[BaseModel] = TopupInput
200
+ client: ProvisionStackClient
201
+
202
+ def _run(self, amount_usd: float, rail: str = "stripe") -> dict[str, Any]:
203
+ if rail == "xrp":
204
+ return self.client.topup_xrp(amount_usd)
205
+ return self.client.topup_stripe(amount_usd)
206
+
207
+
208
+ def get_provision_stack_tools(
209
+ api_key: str | None = None,
210
+ base_url: str | None = None,
211
+ sandbox: bool = False,
212
+ ) -> list[BaseTool]:
213
+ """
214
+ Get all Provision Stack tools for LangChain.
215
+
216
+ Parameters
217
+ ----------
218
+ api_key : str, optional
219
+ API token. Falls back to PROVISION_STACK_API_KEY env var.
220
+ base_url : str, optional
221
+ API base URL. Falls back to env var or default.
222
+ sandbox : bool
223
+ If True, use sandbox (mock Terraform, no real deploys).
224
+
225
+ Returns
226
+ -------
227
+ list[BaseTool]
228
+ LangChain Tool instances ready to pass to an agent.
229
+ """
230
+ client = ProvisionStackClient(
231
+ api_key=api_key, base_url=base_url, sandbox=sandbox
232
+ )
233
+
234
+ return [
235
+ ProvisionStackSuggestTool(client=client),
236
+ ProvisionStackDeployTool(client=client),
237
+ ProvisionStackStatusTool(client=client),
238
+ ProvisionStackDestroyTool(client=client),
239
+ ProvisionStackBalanceTool(client=client),
240
+ ProvisionStackTopupTool(client=client),
241
+ ]
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "provision-stack-langchain"
7
+ version = "0.1.0"
8
+ description = "LangChain tools for Provision Stack — deploy cloud infrastructure via AI agents."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "Provision Stack" },
14
+ ]
15
+ keywords = [
16
+ "langchain",
17
+ "ai-agents",
18
+ "infrastructure",
19
+ "aws",
20
+ "gcp",
21
+ "mcp",
22
+ ]
23
+ classifiers = [
24
+ "Development Status :: 4 - Beta",
25
+ "Intended Audience :: Developers",
26
+ "License :: OSI Approved :: MIT License",
27
+ "Programming Language :: Python :: 3",
28
+ "Programming Language :: Python :: 3.10",
29
+ "Programming Language :: Python :: 3.11",
30
+ "Programming Language :: Python :: 3.12",
31
+ "Programming Language :: Python :: 3.13",
32
+ "Topic :: Software Development :: Libraries :: Python Modules",
33
+ ]
34
+ dependencies = [
35
+ "provision-stack>=0.1.0",
36
+ "langchain-core>=0.3.0",
37
+ ]
38
+
39
+ [project.urls]
40
+ Homepage = "https://provision-stack.com"
41
+ Documentation = "https://docs.provision-stack.com"
42
+ Repository = "https://github.com/TravisLinkey/provision-stack"
43
+
44
+ [project.optional-dependencies]
45
+ test = [
46
+ "pytest>=8.0",
47
+ "pytest-mock>=3.12",
48
+ ]
49
+
50
+ [tool.pytest.ini_options]
51
+ testpaths = ["tests"]
@@ -0,0 +1,117 @@
1
+ """Tests for provision_stack_langchain.tools — LangChain tool wrappers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ import types
7
+ from typing import Any, Type
8
+ import pytest
9
+
10
+
11
+ # ── Mock setup ────────────────────────────────────────────────────
12
+ # LangChain's BaseTool uses Pydantic metaclass magic; we mock it
13
+ # to avoid pulling in langchain-core while keeping class inheritance.
14
+
15
+
16
+ class MockBaseModel:
17
+ """Stand-in for pydantic.BaseModel."""
18
+ model_config: dict[str, Any] = {}
19
+
20
+ def __init__(self, **kwargs: Any) -> None:
21
+ for k, v in kwargs.items():
22
+ setattr(self, k, v)
23
+
24
+
25
+ def mock_field(*args: Any, **kwargs: Any) -> Any:
26
+ return kwargs.get("default", args[0] if args else None)
27
+
28
+
29
+ class MockBaseTool:
30
+ """Stand-in for langchain_core.tools.BaseTool — supports subclassing."""
31
+
32
+ name: str = ""
33
+ description: str = ""
34
+ args_schema: Type[MockBaseModel] | None = None
35
+ client: Any = None
36
+
37
+ def __init__(self, **kwargs: Any) -> None:
38
+ for k, v in kwargs.items():
39
+ setattr(self, k, v)
40
+
41
+ def _run(self, **kwargs: Any) -> Any:
42
+ raise NotImplementedError
43
+
44
+
45
+ # Install mocks before importing our modules
46
+ lc_tools = types.ModuleType("langchain_core.tools")
47
+ lc_tools.BaseTool = MockBaseTool
48
+
49
+ lc = types.ModuleType("langchain_core")
50
+ lc.tools = lc_tools
51
+
52
+ pydantic = types.ModuleType("pydantic")
53
+ pydantic.BaseModel = MockBaseModel
54
+ pydantic.Field = mock_field
55
+
56
+ sys.modules.setdefault("langchain_core", lc)
57
+ sys.modules.setdefault("langchain_core.tools", lc_tools)
58
+ sys.modules.setdefault("pydantic", pydantic)
59
+
60
+ from provision_stack_langchain.tools import get_provision_stack_tools
61
+
62
+
63
+ # ── Tool factory ──────────────────────────────────────────────────
64
+
65
+
66
+ class TestGetProvisionStackTools:
67
+ def test_returns_list(self):
68
+ tools = get_provision_stack_tools(api_key="test")
69
+ assert isinstance(tools, list)
70
+
71
+ def test_returns_six_tools(self):
72
+ tools = get_provision_stack_tools(api_key="test")
73
+ assert len(tools) == 6
74
+
75
+ def test_tool_names(self):
76
+ tools = get_provision_stack_tools(api_key="test")
77
+ names = {t.name for t in tools}
78
+ assert names == {
79
+ "provision_stack_suggest",
80
+ "provision_stack_deploy",
81
+ "provision_stack_status",
82
+ "provision_stack_destroy",
83
+ "provision_stack_balance",
84
+ "provision_stack_topup",
85
+ }
86
+
87
+ def test_tools_have_descriptions(self):
88
+ tools = get_provision_stack_tools(api_key="test")
89
+ for tool in tools:
90
+ assert tool.description
91
+ assert len(tool.description) > 10
92
+
93
+ def test_tools_have_args_schema(self):
94
+ tools = get_provision_stack_tools(api_key="test")
95
+ # All tools except balance have an args_schema (balance takes no input)
96
+ for tool in tools:
97
+ if tool.name == "provision_stack_balance":
98
+ assert tool.args_schema is None
99
+ else:
100
+ assert tool.args_schema is not None
101
+
102
+ def test_tools_share_client(self):
103
+ tools = get_provision_stack_tools(api_key="test")
104
+ clients = {id(t.client) for t in tools}
105
+ assert len(clients) == 1
106
+
107
+ def test_sandbox_mode(self):
108
+ from provision_stack.config import SANDBOX_URL
109
+
110
+ tools = get_provision_stack_tools(api_key="test", sandbox=True)
111
+ assert tools[0].client.base_url == SANDBOX_URL
112
+
113
+ def test_custom_base_url(self):
114
+ tools = get_provision_stack_tools(
115
+ api_key="test", base_url="https://custom.api.com"
116
+ )
117
+ assert tools[0].client.base_url == "https://custom.api.com"