intentkit 0.6.0.dev13__py3-none-any.whl → 0.6.0.dev15__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.
Potentially problematic release.
This version of intentkit might be problematic. Click here for more details.
- intentkit/__init__.py +1 -1
- intentkit/core/engine.py +2 -1
- intentkit/core/node.py +3 -1
- intentkit/models/agent_schema.json +8 -0
- intentkit/skills/base.py +37 -17
- intentkit/skills/cryptocompare/fetch_news.py +2 -2
- intentkit/skills/cryptocompare/fetch_price.py +2 -2
- intentkit/skills/cryptocompare/fetch_top_exchanges.py +2 -2
- intentkit/skills/cryptocompare/fetch_top_market_cap.py +2 -2
- intentkit/skills/cryptocompare/fetch_top_volume.py +2 -2
- intentkit/skills/cryptocompare/fetch_trading_signals.py +2 -2
- intentkit/skills/defillama/base.py +3 -3
- intentkit/skills/enso/base.py +2 -2
- intentkit/skills/enso/networks.py +1 -1
- intentkit/skills/enso/route.py +1 -1
- intentkit/skills/enso/tokens.py +1 -1
- intentkit/skills/firecrawl/clear.py +1 -1
- intentkit/skills/firecrawl/crawl.py +2 -10
- intentkit/skills/firecrawl/query.py +4 -4
- intentkit/skills/firecrawl/scrape.py +2 -8
- intentkit/skills/heurist/image_generation_animagine_xl.py +1 -1
- intentkit/skills/heurist/image_generation_arthemy_comics.py +1 -1
- intentkit/skills/heurist/image_generation_arthemy_real.py +1 -1
- intentkit/skills/heurist/image_generation_braindance.py +1 -1
- intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +1 -1
- intentkit/skills/heurist/image_generation_flux_1_dev.py +1 -1
- intentkit/skills/heurist/image_generation_sdxl.py +1 -1
- intentkit/skills/http/README.md +78 -0
- intentkit/skills/http/__init__.py +100 -0
- intentkit/skills/http/base.py +21 -0
- intentkit/skills/http/get.py +96 -0
- intentkit/skills/http/http.svg +15 -0
- intentkit/skills/http/post.py +113 -0
- intentkit/skills/http/put.py +113 -0
- intentkit/skills/http/schema.json +92 -0
- intentkit/skills/lifi/token_execute.py +1 -1
- intentkit/skills/openai/dalle_image_generation.py +1 -1
- intentkit/skills/openai/gpt_image_generation.py +1 -1
- intentkit/skills/openai/gpt_image_to_image.py +1 -1
- intentkit/skills/supabase/__init__.py +116 -0
- intentkit/skills/supabase/base.py +72 -0
- intentkit/skills/supabase/delete_data.py +102 -0
- intentkit/skills/supabase/fetch_data.py +120 -0
- intentkit/skills/supabase/insert_data.py +70 -0
- intentkit/skills/supabase/invoke_function.py +74 -0
- intentkit/skills/supabase/schema.json +168 -0
- intentkit/skills/supabase/supabase.svg +15 -0
- intentkit/skills/supabase/update_data.py +105 -0
- intentkit/skills/supabase/upsert_data.py +77 -0
- intentkit/skills/system/read_agent_api_key.py +1 -1
- intentkit/skills/system/regenerate_agent_api_key.py +1 -1
- intentkit/skills/token/base.py +1 -39
- intentkit/skills/twitter/follow_user.py +3 -3
- intentkit/skills/twitter/get_mentions.py +6 -6
- intentkit/skills/twitter/get_timeline.py +5 -5
- intentkit/skills/twitter/get_user_by_username.py +3 -3
- intentkit/skills/twitter/get_user_tweets.py +5 -5
- intentkit/skills/twitter/like_tweet.py +3 -3
- intentkit/skills/twitter/post_tweet.py +4 -4
- intentkit/skills/twitter/reply_tweet.py +4 -4
- intentkit/skills/twitter/retweet.py +3 -3
- intentkit/skills/twitter/search_tweets.py +5 -5
- intentkit/skills/unrealspeech/text_to_speech.py +1 -1
- intentkit/skills/web_scraper/document_indexer.py +2 -2
- intentkit/skills/web_scraper/scrape_and_index.py +8 -8
- intentkit/skills/web_scraper/website_indexer.py +4 -4
- {intentkit-0.6.0.dev13.dist-info → intentkit-0.6.0.dev15.dist-info}/METADATA +1 -1
- {intentkit-0.6.0.dev13.dist-info → intentkit-0.6.0.dev15.dist-info}/RECORD +70 -52
- {intentkit-0.6.0.dev13.dist-info → intentkit-0.6.0.dev15.dist-info}/WHEEL +0 -0
- {intentkit-0.6.0.dev13.dist-info → intentkit-0.6.0.dev15.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from typing import Type
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from intentkit.abstracts.skill import SkillStoreABC
|
|
6
|
+
from intentkit.skills.base import IntentKitSkill
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class HttpBaseTool(IntentKitSkill):
|
|
10
|
+
"""Base class for HTTP client tools."""
|
|
11
|
+
|
|
12
|
+
name: str = Field(description="The name of the tool")
|
|
13
|
+
description: str = Field(description="A description of what the tool does")
|
|
14
|
+
args_schema: Type[BaseModel]
|
|
15
|
+
skill_store: SkillStoreABC = Field(
|
|
16
|
+
description="The skill store for persisting data"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def category(self) -> str:
|
|
21
|
+
return "http"
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Dict, Optional, Type
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
from langchain_core.runnables import RunnableConfig
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
from intentkit.skills.http.base import HttpBaseTool
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class HttpGetInput(BaseModel):
|
|
14
|
+
"""Input for HTTP GET request."""
|
|
15
|
+
|
|
16
|
+
url: str = Field(description="The URL to send the GET request to")
|
|
17
|
+
headers: Optional[Dict[str, str]] = Field(
|
|
18
|
+
description="Optional headers to include in the request",
|
|
19
|
+
default=None,
|
|
20
|
+
)
|
|
21
|
+
params: Optional[Dict[str, Any]] = Field(
|
|
22
|
+
description="Optional query parameters to include in the request",
|
|
23
|
+
default=None,
|
|
24
|
+
)
|
|
25
|
+
timeout: Optional[float] = Field(
|
|
26
|
+
description="Request timeout in seconds (default: 30)",
|
|
27
|
+
default=30.0,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class HttpGet(HttpBaseTool):
|
|
32
|
+
"""Tool for making HTTP GET requests.
|
|
33
|
+
|
|
34
|
+
This tool allows you to make HTTP GET requests to any URL with optional
|
|
35
|
+
headers and query parameters. It returns the response content as a string.
|
|
36
|
+
|
|
37
|
+
Attributes:
|
|
38
|
+
name: The name of the tool.
|
|
39
|
+
description: A description of what the tool does.
|
|
40
|
+
args_schema: The schema for the tool's input arguments.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
name: str = "http_get"
|
|
44
|
+
description: str = (
|
|
45
|
+
"Make an HTTP GET request to a specified URL. "
|
|
46
|
+
"You can include custom headers and query parameters. "
|
|
47
|
+
"Returns the response content as text. "
|
|
48
|
+
"Use this when you need to fetch data from web APIs or websites."
|
|
49
|
+
)
|
|
50
|
+
args_schema: Type[BaseModel] = HttpGetInput
|
|
51
|
+
|
|
52
|
+
async def _arun(
|
|
53
|
+
self,
|
|
54
|
+
url: str,
|
|
55
|
+
headers: Optional[Dict[str, str]] = None,
|
|
56
|
+
params: Optional[Dict[str, Any]] = None,
|
|
57
|
+
timeout: float = 30.0,
|
|
58
|
+
config: RunnableConfig = None,
|
|
59
|
+
**kwargs,
|
|
60
|
+
) -> str:
|
|
61
|
+
"""Implementation of the HTTP GET request.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
url: The URL to send the GET request to.
|
|
65
|
+
headers: Optional headers to include in the request.
|
|
66
|
+
params: Optional query parameters to include in the request.
|
|
67
|
+
timeout: Request timeout in seconds.
|
|
68
|
+
config: The runnable config (unused but required by interface).
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
str: The response content as text, or error message if request fails.
|
|
72
|
+
"""
|
|
73
|
+
try:
|
|
74
|
+
async with httpx.AsyncClient() as client:
|
|
75
|
+
response = await client.get(
|
|
76
|
+
url=url,
|
|
77
|
+
headers=headers,
|
|
78
|
+
params=params,
|
|
79
|
+
timeout=timeout,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Raise an exception for bad status codes
|
|
83
|
+
response.raise_for_status()
|
|
84
|
+
|
|
85
|
+
# Return response content
|
|
86
|
+
return f"Status: {response.status_code}\nContent: {response.text}"
|
|
87
|
+
|
|
88
|
+
except httpx.TimeoutException:
|
|
89
|
+
return f"Error: Request to {url} timed out after {timeout} seconds"
|
|
90
|
+
except httpx.HTTPStatusError as e:
|
|
91
|
+
return f"Error: HTTP {e.response.status_code} - {e.response.text}"
|
|
92
|
+
except httpx.RequestError as e:
|
|
93
|
+
return f"Error: Failed to connect to {url} - {str(e)}"
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.error(f"Unexpected error in HTTP GET request: {e}")
|
|
96
|
+
return f"Error: Unexpected error occurred - {str(e)}"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
|
2
|
+
<!-- Background -->
|
|
3
|
+
<rect width="100" height="100" fill="#2563eb" rx="15"/>
|
|
4
|
+
|
|
5
|
+
<!-- HTTP text -->
|
|
6
|
+
<text x="50" y="35" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="white" text-anchor="middle">HTTP</text>
|
|
7
|
+
|
|
8
|
+
<!-- Arrow indicating request/response -->
|
|
9
|
+
<path d="M20 50 L35 45 L35 48 L65 48 L65 45 L80 50 L65 55 L65 52 L35 52 L35 55 Z" fill="white"/>
|
|
10
|
+
|
|
11
|
+
<!-- Globe/network icon -->
|
|
12
|
+
<circle cx="50" cy="75" r="12" fill="none" stroke="white" stroke-width="2"/>
|
|
13
|
+
<path d="M38 75 Q50 65 62 75 M38 75 Q50 85 62 75" fill="none" stroke="white" stroke-width="1.5"/>
|
|
14
|
+
<line x1="50" y1="63" x2="50" y2="87" stroke="white" stroke-width="1.5"/>
|
|
15
|
+
</svg>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Dict, Optional, Type, Union
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
from langchain_core.runnables import RunnableConfig
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
from intentkit.skills.http.base import HttpBaseTool
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class HttpPostInput(BaseModel):
|
|
14
|
+
"""Input for HTTP POST request."""
|
|
15
|
+
|
|
16
|
+
url: str = Field(description="The URL to send the POST request to")
|
|
17
|
+
data: Optional[Union[Dict[str, Any], str]] = Field(
|
|
18
|
+
description="The data to send in the request body. Can be a dictionary (will be sent as JSON) or a string",
|
|
19
|
+
default=None,
|
|
20
|
+
)
|
|
21
|
+
headers: Optional[Dict[str, str]] = Field(
|
|
22
|
+
description="Optional headers to include in the request",
|
|
23
|
+
default=None,
|
|
24
|
+
)
|
|
25
|
+
params: Optional[Dict[str, Any]] = Field(
|
|
26
|
+
description="Optional query parameters to include in the request",
|
|
27
|
+
default=None,
|
|
28
|
+
)
|
|
29
|
+
timeout: Optional[float] = Field(
|
|
30
|
+
description="Request timeout in seconds (default: 30)",
|
|
31
|
+
default=30.0,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class HttpPost(HttpBaseTool):
|
|
36
|
+
"""Tool for making HTTP POST requests.
|
|
37
|
+
|
|
38
|
+
This tool allows you to make HTTP POST requests to any URL with optional
|
|
39
|
+
headers, query parameters, and request body data. It returns the response content as a string.
|
|
40
|
+
|
|
41
|
+
Attributes:
|
|
42
|
+
name: The name of the tool.
|
|
43
|
+
description: A description of what the tool does.
|
|
44
|
+
args_schema: The schema for the tool's input arguments.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
name: str = "http_post"
|
|
48
|
+
description: str = (
|
|
49
|
+
"Make an HTTP POST request to a specified URL. "
|
|
50
|
+
"You can include custom headers, query parameters, and request body data. "
|
|
51
|
+
"Data can be provided as a dictionary (sent as JSON) or as a string. "
|
|
52
|
+
"Returns the response content as text. "
|
|
53
|
+
"Use this when you need to send data to web APIs or submit forms."
|
|
54
|
+
)
|
|
55
|
+
args_schema: Type[BaseModel] = HttpPostInput
|
|
56
|
+
|
|
57
|
+
async def _arun(
|
|
58
|
+
self,
|
|
59
|
+
url: str,
|
|
60
|
+
data: Optional[Union[Dict[str, Any], str]] = None,
|
|
61
|
+
headers: Optional[Dict[str, str]] = None,
|
|
62
|
+
params: Optional[Dict[str, Any]] = None,
|
|
63
|
+
timeout: float = 30.0,
|
|
64
|
+
config: RunnableConfig = None,
|
|
65
|
+
**kwargs,
|
|
66
|
+
) -> str:
|
|
67
|
+
"""Implementation of the HTTP POST request.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
url: The URL to send the POST request to.
|
|
71
|
+
data: The data to send in the request body.
|
|
72
|
+
headers: Optional headers to include in the request.
|
|
73
|
+
params: Optional query parameters to include in the request.
|
|
74
|
+
timeout: Request timeout in seconds.
|
|
75
|
+
config: The runnable config (unused but required by interface).
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
str: The response content as text, or error message if request fails.
|
|
79
|
+
"""
|
|
80
|
+
try:
|
|
81
|
+
# Prepare headers
|
|
82
|
+
request_headers = headers or {}
|
|
83
|
+
|
|
84
|
+
# If data is a dictionary, send as JSON
|
|
85
|
+
if isinstance(data, dict):
|
|
86
|
+
if "content-type" not in {k.lower() for k in request_headers.keys()}:
|
|
87
|
+
request_headers["Content-Type"] = "application/json"
|
|
88
|
+
|
|
89
|
+
async with httpx.AsyncClient() as client:
|
|
90
|
+
response = await client.post(
|
|
91
|
+
url=url,
|
|
92
|
+
json=data if isinstance(data, dict) else None,
|
|
93
|
+
content=data if isinstance(data, str) else None,
|
|
94
|
+
headers=request_headers,
|
|
95
|
+
params=params,
|
|
96
|
+
timeout=timeout,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Raise an exception for bad status codes
|
|
100
|
+
response.raise_for_status()
|
|
101
|
+
|
|
102
|
+
# Return response content
|
|
103
|
+
return f"Status: {response.status_code}\nContent: {response.text}"
|
|
104
|
+
|
|
105
|
+
except httpx.TimeoutException:
|
|
106
|
+
return f"Error: Request to {url} timed out after {timeout} seconds"
|
|
107
|
+
except httpx.HTTPStatusError as e:
|
|
108
|
+
return f"Error: HTTP {e.response.status_code} - {e.response.text}"
|
|
109
|
+
except httpx.RequestError as e:
|
|
110
|
+
return f"Error: Failed to connect to {url} - {str(e)}"
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.error(f"Unexpected error in HTTP POST request: {e}")
|
|
113
|
+
return f"Error: Unexpected error occurred - {str(e)}"
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Dict, Optional, Type, Union
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
from langchain_core.runnables import RunnableConfig
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
from intentkit.skills.http.base import HttpBaseTool
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class HttpPutInput(BaseModel):
|
|
14
|
+
"""Input for HTTP PUT request."""
|
|
15
|
+
|
|
16
|
+
url: str = Field(description="The URL to send the PUT request to")
|
|
17
|
+
data: Optional[Union[Dict[str, Any], str]] = Field(
|
|
18
|
+
description="The data to send in the request body. Can be a dictionary (will be sent as JSON) or a string",
|
|
19
|
+
default=None,
|
|
20
|
+
)
|
|
21
|
+
headers: Optional[Dict[str, str]] = Field(
|
|
22
|
+
description="Optional headers to include in the request",
|
|
23
|
+
default=None,
|
|
24
|
+
)
|
|
25
|
+
params: Optional[Dict[str, Any]] = Field(
|
|
26
|
+
description="Optional query parameters to include in the request",
|
|
27
|
+
default=None,
|
|
28
|
+
)
|
|
29
|
+
timeout: Optional[float] = Field(
|
|
30
|
+
description="Request timeout in seconds (default: 30)",
|
|
31
|
+
default=30.0,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class HttpPut(HttpBaseTool):
|
|
36
|
+
"""Tool for making HTTP PUT requests.
|
|
37
|
+
|
|
38
|
+
This tool allows you to make HTTP PUT requests to any URL with optional
|
|
39
|
+
headers, query parameters, and request body data. It returns the response content as a string.
|
|
40
|
+
|
|
41
|
+
Attributes:
|
|
42
|
+
name: The name of the tool.
|
|
43
|
+
description: A description of what the tool does.
|
|
44
|
+
args_schema: The schema for the tool's input arguments.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
name: str = "http_put"
|
|
48
|
+
description: str = (
|
|
49
|
+
"Make an HTTP PUT request to a specified URL. "
|
|
50
|
+
"You can include custom headers, query parameters, and request body data. "
|
|
51
|
+
"Data can be provided as a dictionary (sent as JSON) or as a string. "
|
|
52
|
+
"Returns the response content as text. "
|
|
53
|
+
"Use this when you need to update or replace data on web APIs."
|
|
54
|
+
)
|
|
55
|
+
args_schema: Type[BaseModel] = HttpPutInput
|
|
56
|
+
|
|
57
|
+
async def _arun(
|
|
58
|
+
self,
|
|
59
|
+
url: str,
|
|
60
|
+
data: Optional[Union[Dict[str, Any], str]] = None,
|
|
61
|
+
headers: Optional[Dict[str, str]] = None,
|
|
62
|
+
params: Optional[Dict[str, Any]] = None,
|
|
63
|
+
timeout: float = 30.0,
|
|
64
|
+
config: RunnableConfig = None,
|
|
65
|
+
**kwargs,
|
|
66
|
+
) -> str:
|
|
67
|
+
"""Implementation of the HTTP PUT request.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
url: The URL to send the PUT request to.
|
|
71
|
+
data: The data to send in the request body.
|
|
72
|
+
headers: Optional headers to include in the request.
|
|
73
|
+
params: Optional query parameters to include in the request.
|
|
74
|
+
timeout: Request timeout in seconds.
|
|
75
|
+
config: The runnable config (unused but required by interface).
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
str: The response content as text, or error message if request fails.
|
|
79
|
+
"""
|
|
80
|
+
try:
|
|
81
|
+
# Prepare headers
|
|
82
|
+
request_headers = headers or {}
|
|
83
|
+
|
|
84
|
+
# If data is a dictionary, send as JSON
|
|
85
|
+
if isinstance(data, dict):
|
|
86
|
+
if "content-type" not in {k.lower() for k in request_headers.keys()}:
|
|
87
|
+
request_headers["Content-Type"] = "application/json"
|
|
88
|
+
|
|
89
|
+
async with httpx.AsyncClient() as client:
|
|
90
|
+
response = await client.put(
|
|
91
|
+
url=url,
|
|
92
|
+
json=data if isinstance(data, dict) else None,
|
|
93
|
+
content=data if isinstance(data, str) else None,
|
|
94
|
+
headers=request_headers,
|
|
95
|
+
params=params,
|
|
96
|
+
timeout=timeout,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Raise an exception for bad status codes
|
|
100
|
+
response.raise_for_status()
|
|
101
|
+
|
|
102
|
+
# Return response content
|
|
103
|
+
return f"Status: {response.status_code}\nContent: {response.text}"
|
|
104
|
+
|
|
105
|
+
except httpx.TimeoutException:
|
|
106
|
+
return f"Error: Request to {url} timed out after {timeout} seconds"
|
|
107
|
+
except httpx.HTTPStatusError as e:
|
|
108
|
+
return f"Error: HTTP {e.response.status_code} - {e.response.text}"
|
|
109
|
+
except httpx.RequestError as e:
|
|
110
|
+
return f"Error: Failed to connect to {url} - {str(e)}"
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.error(f"Unexpected error in HTTP PUT request: {e}")
|
|
113
|
+
return f"Error: Unexpected error occurred - {str(e)}"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"title": "HTTP Client",
|
|
5
|
+
"description": "HTTP client skills for making web requests",
|
|
6
|
+
"x-icon": "https://ai.service.crestal.dev/skills/http/http.jpg",
|
|
7
|
+
"x-tags": [
|
|
8
|
+
"HTTP",
|
|
9
|
+
"Web",
|
|
10
|
+
"API",
|
|
11
|
+
"Client"
|
|
12
|
+
],
|
|
13
|
+
"properties": {
|
|
14
|
+
"enabled": {
|
|
15
|
+
"type": "boolean",
|
|
16
|
+
"title": "Enabled",
|
|
17
|
+
"description": "Whether this skill is enabled",
|
|
18
|
+
"default": true
|
|
19
|
+
},
|
|
20
|
+
"states": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"http_get": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"title": "HTTP GET",
|
|
26
|
+
"enum": [
|
|
27
|
+
"disabled",
|
|
28
|
+
"public",
|
|
29
|
+
"private"
|
|
30
|
+
],
|
|
31
|
+
"x-enum-title": [
|
|
32
|
+
"Disabled",
|
|
33
|
+
"Agent Owner + All Users",
|
|
34
|
+
"Agent Owner Only"
|
|
35
|
+
],
|
|
36
|
+
"description": "Make HTTP GET requests to fetch data from web APIs and websites",
|
|
37
|
+
"default": "private"
|
|
38
|
+
},
|
|
39
|
+
"http_post": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"title": "HTTP POST",
|
|
42
|
+
"enum": [
|
|
43
|
+
"disabled",
|
|
44
|
+
"public",
|
|
45
|
+
"private"
|
|
46
|
+
],
|
|
47
|
+
"x-enum-title": [
|
|
48
|
+
"Disabled",
|
|
49
|
+
"Agent Owner + All Users",
|
|
50
|
+
"Agent Owner Only"
|
|
51
|
+
],
|
|
52
|
+
"description": "Make HTTP POST requests to send data to web APIs and submit forms",
|
|
53
|
+
"default": "private"
|
|
54
|
+
},
|
|
55
|
+
"http_put": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"title": "HTTP PUT",
|
|
58
|
+
"enum": [
|
|
59
|
+
"disabled",
|
|
60
|
+
"public",
|
|
61
|
+
"private"
|
|
62
|
+
],
|
|
63
|
+
"x-enum-title": [
|
|
64
|
+
"Disabled",
|
|
65
|
+
"Agent Owner + All Users",
|
|
66
|
+
"Agent Owner Only"
|
|
67
|
+
],
|
|
68
|
+
"description": "Make HTTP PUT requests to update or replace data on web APIs",
|
|
69
|
+
"default": "private"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"description": "States for each HTTP client skill (disabled, public, or private)"
|
|
73
|
+
},
|
|
74
|
+
"api_key_provider": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"title": "API Key Provider",
|
|
77
|
+
"description": "Who provides the API key",
|
|
78
|
+
"enum": [
|
|
79
|
+
"platform"
|
|
80
|
+
],
|
|
81
|
+
"x-enum-title": [
|
|
82
|
+
"Nation Hosted"
|
|
83
|
+
],
|
|
84
|
+
"default": "platform"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"required": [
|
|
88
|
+
"states",
|
|
89
|
+
"enabled"
|
|
90
|
+
],
|
|
91
|
+
"additionalProperties": true
|
|
92
|
+
}
|
|
@@ -129,7 +129,7 @@ class TokenExecute(LiFiBaseTool):
|
|
|
129
129
|
|
|
130
130
|
# Get agent context for CDP wallet
|
|
131
131
|
context = self.context_from_config(config)
|
|
132
|
-
agent_id = context.
|
|
132
|
+
agent_id = context.agent_id
|
|
133
133
|
|
|
134
134
|
self.logger.info(
|
|
135
135
|
f"Executing LiFi transfer: {from_amount} {from_token} on {from_chain} -> {to_token} on {to_chain}"
|
|
@@ -109,7 +109,7 @@ class DALLEImageGeneration(OpenAIBaseTool):
|
|
|
109
109
|
image_url = image_url.strip('"')
|
|
110
110
|
|
|
111
111
|
# Generate a key with agent ID as prefix
|
|
112
|
-
image_key = f"{context.
|
|
112
|
+
image_key = f"{context.agent_id}/dalle/{job_id}"
|
|
113
113
|
|
|
114
114
|
# Store the image and get the CDN URL
|
|
115
115
|
stored_url = await store_image(image_url, image_key)
|
|
@@ -133,7 +133,7 @@ class GPTImageGeneration(OpenAIBaseTool):
|
|
|
133
133
|
image_bytes = base64.b64decode(base64_image)
|
|
134
134
|
|
|
135
135
|
# Generate a key with agent ID as prefix
|
|
136
|
-
image_key = f"{context.
|
|
136
|
+
image_key = f"{context.agent_id}/gpt-image/{job_id}"
|
|
137
137
|
|
|
138
138
|
# Store the image bytes and get the CDN URL
|
|
139
139
|
stored_url = await store_image_bytes(image_bytes, image_key, content_type)
|
|
@@ -157,7 +157,7 @@ class GPTImageToImage(OpenAIBaseTool):
|
|
|
157
157
|
image_bytes = base64.b64decode(base64_image)
|
|
158
158
|
|
|
159
159
|
# Generate a key with agent ID as prefix
|
|
160
|
-
image_key = f"{context.
|
|
160
|
+
image_key = f"{context.agent_id}/gpt-image-edit/{job_id}"
|
|
161
161
|
|
|
162
162
|
# Store the image bytes and get the CDN URL
|
|
163
163
|
stored_url = await store_image_bytes(image_bytes, image_key)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Supabase skills."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import TypedDict
|
|
5
|
+
|
|
6
|
+
from intentkit.abstracts.skill import SkillStoreABC
|
|
7
|
+
from intentkit.skills.base import SkillConfig, SkillState
|
|
8
|
+
from intentkit.skills.supabase.base import SupabaseBaseTool
|
|
9
|
+
from intentkit.skills.supabase.delete_data import SupabaseDeleteData
|
|
10
|
+
from intentkit.skills.supabase.fetch_data import SupabaseFetchData
|
|
11
|
+
from intentkit.skills.supabase.insert_data import SupabaseInsertData
|
|
12
|
+
from intentkit.skills.supabase.invoke_function import SupabaseInvokeFunction
|
|
13
|
+
from intentkit.skills.supabase.update_data import SupabaseUpdateData
|
|
14
|
+
from intentkit.skills.supabase.upsert_data import SupabaseUpsertData
|
|
15
|
+
|
|
16
|
+
# Cache skills at the system level, because they are stateless
|
|
17
|
+
_cache: dict[str, SupabaseBaseTool] = {}
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SkillStates(TypedDict):
|
|
23
|
+
fetch_data: SkillState
|
|
24
|
+
insert_data: SkillState
|
|
25
|
+
update_data: SkillState
|
|
26
|
+
upsert_data: SkillState
|
|
27
|
+
delete_data: SkillState
|
|
28
|
+
invoke_function: SkillState
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Config(SkillConfig):
|
|
32
|
+
"""Configuration for Supabase skills."""
|
|
33
|
+
|
|
34
|
+
states: SkillStates
|
|
35
|
+
supabase_url: str
|
|
36
|
+
supabase_key: str
|
|
37
|
+
public_write_tables: str = ""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def get_skills(
|
|
41
|
+
config: "Config",
|
|
42
|
+
is_private: bool,
|
|
43
|
+
store: SkillStoreABC,
|
|
44
|
+
**_,
|
|
45
|
+
) -> list[SupabaseBaseTool]:
|
|
46
|
+
"""Get all Supabase skills."""
|
|
47
|
+
available_skills = []
|
|
48
|
+
|
|
49
|
+
# Include skills based on their state
|
|
50
|
+
for skill_name, state in config["states"].items():
|
|
51
|
+
if state == "disabled":
|
|
52
|
+
continue
|
|
53
|
+
elif state == "public" or (state == "private" and is_private):
|
|
54
|
+
available_skills.append(skill_name)
|
|
55
|
+
|
|
56
|
+
# Get each skill using the cached getter
|
|
57
|
+
result = []
|
|
58
|
+
for name in available_skills:
|
|
59
|
+
skill = get_supabase_skill(name, store)
|
|
60
|
+
if skill:
|
|
61
|
+
result.append(skill)
|
|
62
|
+
return result
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def get_supabase_skill(
|
|
66
|
+
name: str,
|
|
67
|
+
store: SkillStoreABC,
|
|
68
|
+
) -> SupabaseBaseTool:
|
|
69
|
+
"""Get a Supabase skill by name.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
name: The name of the skill to get
|
|
73
|
+
store: The skill store for persisting data
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
The requested Supabase skill
|
|
77
|
+
"""
|
|
78
|
+
if name == "fetch_data":
|
|
79
|
+
if name not in _cache:
|
|
80
|
+
_cache[name] = SupabaseFetchData(
|
|
81
|
+
skill_store=store,
|
|
82
|
+
)
|
|
83
|
+
return _cache[name]
|
|
84
|
+
elif name == "insert_data":
|
|
85
|
+
if name not in _cache:
|
|
86
|
+
_cache[name] = SupabaseInsertData(
|
|
87
|
+
skill_store=store,
|
|
88
|
+
)
|
|
89
|
+
return _cache[name]
|
|
90
|
+
elif name == "update_data":
|
|
91
|
+
if name not in _cache:
|
|
92
|
+
_cache[name] = SupabaseUpdateData(
|
|
93
|
+
skill_store=store,
|
|
94
|
+
)
|
|
95
|
+
return _cache[name]
|
|
96
|
+
elif name == "upsert_data":
|
|
97
|
+
if name not in _cache:
|
|
98
|
+
_cache[name] = SupabaseUpsertData(
|
|
99
|
+
skill_store=store,
|
|
100
|
+
)
|
|
101
|
+
return _cache[name]
|
|
102
|
+
elif name == "delete_data":
|
|
103
|
+
if name not in _cache:
|
|
104
|
+
_cache[name] = SupabaseDeleteData(
|
|
105
|
+
skill_store=store,
|
|
106
|
+
)
|
|
107
|
+
return _cache[name]
|
|
108
|
+
elif name == "invoke_function":
|
|
109
|
+
if name not in _cache:
|
|
110
|
+
_cache[name] = SupabaseInvokeFunction(
|
|
111
|
+
skill_store=store,
|
|
112
|
+
)
|
|
113
|
+
return _cache[name]
|
|
114
|
+
else:
|
|
115
|
+
logger.warning(f"Unknown Supabase skill: {name}")
|
|
116
|
+
return None
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from typing import Type
|
|
2
|
+
|
|
3
|
+
from langchain_core.tools import ToolException
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
from intentkit.abstracts.skill import SkillStoreABC
|
|
7
|
+
from intentkit.skills.base import IntentKitSkill, SkillContext
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SupabaseBaseTool(IntentKitSkill):
|
|
11
|
+
"""Base class for Supabase tools."""
|
|
12
|
+
|
|
13
|
+
name: str = Field(description="The name of the tool")
|
|
14
|
+
description: str = Field(description="A description of what the tool does")
|
|
15
|
+
args_schema: Type[BaseModel]
|
|
16
|
+
skill_store: SkillStoreABC = Field(
|
|
17
|
+
description="The skill store for persisting data"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def category(self) -> str:
|
|
22
|
+
return "supabase"
|
|
23
|
+
|
|
24
|
+
def get_supabase_config(self, config: dict) -> tuple[str, str]:
|
|
25
|
+
"""Get Supabase URL and key from config.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
config: The agent configuration
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Tuple of (supabase_url, supabase_key)
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
ValueError: If required config is missing
|
|
35
|
+
"""
|
|
36
|
+
supabase_url = config.get("supabase_url")
|
|
37
|
+
supabase_key = config.get("supabase_key")
|
|
38
|
+
|
|
39
|
+
if not supabase_url:
|
|
40
|
+
raise ValueError("supabase_url is required in config")
|
|
41
|
+
if not supabase_key:
|
|
42
|
+
raise ValueError("supabase_key is required in config")
|
|
43
|
+
|
|
44
|
+
return supabase_url, supabase_key
|
|
45
|
+
|
|
46
|
+
def validate_table_access(self, table: str, context: SkillContext) -> None:
|
|
47
|
+
"""Validate if the table can be accessed for write operations in public mode.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
table: The table name to validate
|
|
51
|
+
context: The skill context containing configuration and mode info
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
ToolException: If table access is not allowed in public mode
|
|
55
|
+
"""
|
|
56
|
+
# If in private mode (owner mode), no restrictions apply
|
|
57
|
+
if context.is_private:
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
# In public mode, check if table is in allowed list
|
|
61
|
+
public_write_tables = context.config.get("public_write_tables", "")
|
|
62
|
+
if not public_write_tables:
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
allowed_tables = [
|
|
66
|
+
t.strip() for t in public_write_tables.split(",") if t.strip()
|
|
67
|
+
]
|
|
68
|
+
if table not in allowed_tables:
|
|
69
|
+
raise ToolException(
|
|
70
|
+
f"Table '{table}' is not allowed for public write operations. "
|
|
71
|
+
f"Allowed tables: {', '.join(allowed_tables)}"
|
|
72
|
+
)
|