intentkit 0.6.0.dev12__py3-none-any.whl → 0.6.0.dev14__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/abstracts/skill.py +12 -0
- intentkit/core/engine.py +2 -1
- intentkit/core/node.py +3 -1
- intentkit/core/skill.py +11 -0
- intentkit/models/skill.py +19 -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/README.md +35 -2
- intentkit/skills/firecrawl/__init__.py +8 -0
- intentkit/skills/firecrawl/clear.py +87 -0
- intentkit/skills/firecrawl/crawl.py +2 -10
- intentkit/skills/firecrawl/query.py +4 -4
- intentkit/skills/firecrawl/schema.json +16 -0
- intentkit/skills/firecrawl/scrape.py +2 -8
- intentkit/skills/firecrawl/utils.py +63 -44
- 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/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.dev12.dist-info → intentkit-0.6.0.dev14.dist-info}/METADATA +1 -1
- {intentkit-0.6.0.dev12.dist-info → intentkit-0.6.0.dev14.dist-info}/RECORD +68 -57
- {intentkit-0.6.0.dev12.dist-info → intentkit-0.6.0.dev14.dist-info}/WHEEL +0 -0
- {intentkit-0.6.0.dev12.dist-info → intentkit-0.6.0.dev14.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Dict, Optional, Type
|
|
3
|
+
|
|
4
|
+
from langchain_core.runnables import RunnableConfig
|
|
5
|
+
from langchain_core.tools import ToolException
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
from supabase import Client, create_client
|
|
8
|
+
|
|
9
|
+
from intentkit.skills.supabase.base import SupabaseBaseTool
|
|
10
|
+
|
|
11
|
+
NAME = "supabase_fetch_data"
|
|
12
|
+
PROMPT = "Fetch data from a Supabase table with optional filtering, ordering, and pagination."
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SupabaseFetchDataInput(BaseModel):
|
|
18
|
+
"""Input for SupabaseFetchData tool."""
|
|
19
|
+
|
|
20
|
+
table: str = Field(description="The name of the table to fetch data from")
|
|
21
|
+
columns: Optional[str] = Field(
|
|
22
|
+
default="*",
|
|
23
|
+
description="Comma-separated list of columns to select (default: '*' for all)",
|
|
24
|
+
)
|
|
25
|
+
filters: Optional[Dict[str, Any]] = Field(
|
|
26
|
+
default=None,
|
|
27
|
+
description="Dictionary of filters to apply (e.g., {'column': 'value', 'age': {'gte': 18}})",
|
|
28
|
+
)
|
|
29
|
+
order_by: Optional[str] = Field(default=None, description="Column to order by")
|
|
30
|
+
ascending: bool = Field(
|
|
31
|
+
default=True, description="Whether to order in ascending order (default: True)"
|
|
32
|
+
)
|
|
33
|
+
limit: Optional[int] = Field(
|
|
34
|
+
default=None, description="Maximum number of records to return"
|
|
35
|
+
)
|
|
36
|
+
offset: Optional[int] = Field(
|
|
37
|
+
default=None, description="Number of records to skip for pagination"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SupabaseFetchData(SupabaseBaseTool):
|
|
42
|
+
"""Tool for fetching data from Supabase tables.
|
|
43
|
+
|
|
44
|
+
This tool allows querying Supabase tables with filtering, ordering, and pagination.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
name: str = NAME
|
|
48
|
+
description: str = PROMPT
|
|
49
|
+
args_schema: Type[BaseModel] = SupabaseFetchDataInput
|
|
50
|
+
|
|
51
|
+
async def _arun(
|
|
52
|
+
self,
|
|
53
|
+
table: str,
|
|
54
|
+
columns: Optional[str] = "*",
|
|
55
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
56
|
+
order_by: Optional[str] = None,
|
|
57
|
+
ascending: bool = True,
|
|
58
|
+
limit: Optional[int] = None,
|
|
59
|
+
offset: Optional[int] = None,
|
|
60
|
+
config: RunnableConfig = None,
|
|
61
|
+
**kwargs,
|
|
62
|
+
):
|
|
63
|
+
try:
|
|
64
|
+
context = self.context_from_config(config)
|
|
65
|
+
supabase_url, supabase_key = self.get_supabase_config(context.config)
|
|
66
|
+
|
|
67
|
+
# Create Supabase client
|
|
68
|
+
supabase: Client = create_client(supabase_url, supabase_key)
|
|
69
|
+
|
|
70
|
+
# Start building the query
|
|
71
|
+
query = supabase.table(table).select(columns)
|
|
72
|
+
|
|
73
|
+
# Apply filters if provided
|
|
74
|
+
if filters:
|
|
75
|
+
for column, value in filters.items():
|
|
76
|
+
if isinstance(value, dict):
|
|
77
|
+
# Handle complex filters like {'gte': 18}
|
|
78
|
+
for operator, filter_value in value.items():
|
|
79
|
+
if operator == "eq":
|
|
80
|
+
query = query.eq(column, filter_value)
|
|
81
|
+
elif operator == "neq":
|
|
82
|
+
query = query.neq(column, filter_value)
|
|
83
|
+
elif operator == "gt":
|
|
84
|
+
query = query.gt(column, filter_value)
|
|
85
|
+
elif operator == "gte":
|
|
86
|
+
query = query.gte(column, filter_value)
|
|
87
|
+
elif operator == "lt":
|
|
88
|
+
query = query.lt(column, filter_value)
|
|
89
|
+
elif operator == "lte":
|
|
90
|
+
query = query.lte(column, filter_value)
|
|
91
|
+
elif operator == "like":
|
|
92
|
+
query = query.like(column, filter_value)
|
|
93
|
+
elif operator == "ilike":
|
|
94
|
+
query = query.ilike(column, filter_value)
|
|
95
|
+
elif operator == "in":
|
|
96
|
+
query = query.in_(column, filter_value)
|
|
97
|
+
else:
|
|
98
|
+
logger.warning(f"Unknown filter operator: {operator}")
|
|
99
|
+
else:
|
|
100
|
+
# Simple equality filter
|
|
101
|
+
query = query.eq(column, value)
|
|
102
|
+
|
|
103
|
+
# Apply ordering if provided
|
|
104
|
+
if order_by:
|
|
105
|
+
query = query.order(order_by, desc=not ascending)
|
|
106
|
+
|
|
107
|
+
# Apply pagination
|
|
108
|
+
if limit:
|
|
109
|
+
query = query.limit(limit)
|
|
110
|
+
if offset:
|
|
111
|
+
query = query.offset(offset)
|
|
112
|
+
|
|
113
|
+
# Execute the query
|
|
114
|
+
response = query.execute()
|
|
115
|
+
|
|
116
|
+
return {"success": True, "data": response.data, "count": len(response.data)}
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.error(f"Error fetching data from Supabase: {str(e)}")
|
|
120
|
+
raise ToolException(f"Failed to fetch data from table '{table}': {str(e)}")
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Dict, List, Type, Union
|
|
3
|
+
|
|
4
|
+
from langchain_core.runnables import RunnableConfig
|
|
5
|
+
from langchain_core.tools import ToolException
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
from supabase import Client, create_client
|
|
8
|
+
|
|
9
|
+
from intentkit.skills.supabase.base import SupabaseBaseTool
|
|
10
|
+
|
|
11
|
+
NAME = "supabase_insert_data"
|
|
12
|
+
PROMPT = "Insert new data into a Supabase table."
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SupabaseInsertDataInput(BaseModel):
|
|
18
|
+
"""Input for SupabaseInsertData tool."""
|
|
19
|
+
|
|
20
|
+
table: str = Field(description="The name of the table to insert data into")
|
|
21
|
+
data: Union[Dict[str, Any], List[Dict[str, Any]]] = Field(
|
|
22
|
+
description="The data to insert. Can be a single object or a list of objects"
|
|
23
|
+
)
|
|
24
|
+
returning: str = Field(
|
|
25
|
+
default="*",
|
|
26
|
+
description="Columns to return after insertion (default: '*' for all)",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SupabaseInsertData(SupabaseBaseTool):
|
|
31
|
+
"""Tool for inserting data into Supabase tables.
|
|
32
|
+
|
|
33
|
+
This tool allows inserting single or multiple records into Supabase tables.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
name: str = NAME
|
|
37
|
+
description: str = PROMPT
|
|
38
|
+
args_schema: Type[BaseModel] = SupabaseInsertDataInput
|
|
39
|
+
|
|
40
|
+
async def _arun(
|
|
41
|
+
self,
|
|
42
|
+
table: str,
|
|
43
|
+
data: Union[Dict[str, Any], List[Dict[str, Any]]],
|
|
44
|
+
returning: str = "*",
|
|
45
|
+
config: RunnableConfig = None,
|
|
46
|
+
**kwargs,
|
|
47
|
+
):
|
|
48
|
+
try:
|
|
49
|
+
context = self.context_from_config(config)
|
|
50
|
+
|
|
51
|
+
# Validate table access for public mode
|
|
52
|
+
self.validate_table_access(table, context)
|
|
53
|
+
|
|
54
|
+
supabase_url, supabase_key = self.get_supabase_config(context.config)
|
|
55
|
+
|
|
56
|
+
# Create Supabase client
|
|
57
|
+
supabase: Client = create_client(supabase_url, supabase_key)
|
|
58
|
+
|
|
59
|
+
# Insert data
|
|
60
|
+
response = supabase.table(table).insert(data).execute()
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
"success": True,
|
|
64
|
+
"data": response.data,
|
|
65
|
+
"count": len(response.data) if response.data else 0,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
except Exception as e:
|
|
69
|
+
logger.error(f"Error inserting data into Supabase: {str(e)}")
|
|
70
|
+
raise ToolException(f"Failed to insert data into table '{table}': {str(e)}")
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Dict, Optional, Type
|
|
3
|
+
|
|
4
|
+
from langchain_core.runnables import RunnableConfig
|
|
5
|
+
from langchain_core.tools import ToolException
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
from supabase import Client, create_client
|
|
8
|
+
|
|
9
|
+
from intentkit.skills.supabase.base import SupabaseBaseTool
|
|
10
|
+
|
|
11
|
+
NAME = "supabase_invoke_function"
|
|
12
|
+
PROMPT = "Invoke a Supabase Edge Function with optional parameters."
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SupabaseInvokeFunctionInput(BaseModel):
|
|
18
|
+
"""Input for SupabaseInvokeFunction tool."""
|
|
19
|
+
|
|
20
|
+
function_name: str = Field(description="The name of the Edge Function to invoke")
|
|
21
|
+
parameters: Optional[Dict[str, Any]] = Field(
|
|
22
|
+
default=None, description="Optional parameters to pass to the function"
|
|
23
|
+
)
|
|
24
|
+
headers: Optional[Dict[str, str]] = Field(
|
|
25
|
+
default=None, description="Optional headers to include in the request"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SupabaseInvokeFunction(SupabaseBaseTool):
|
|
30
|
+
"""Tool for invoking Supabase Edge Functions.
|
|
31
|
+
|
|
32
|
+
This tool allows calling Supabase Edge Functions with optional parameters and headers.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
name: str = NAME
|
|
36
|
+
description: str = PROMPT
|
|
37
|
+
args_schema: Type[BaseModel] = SupabaseInvokeFunctionInput
|
|
38
|
+
|
|
39
|
+
async def _arun(
|
|
40
|
+
self,
|
|
41
|
+
function_name: str,
|
|
42
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
43
|
+
headers: Optional[Dict[str, str]] = None,
|
|
44
|
+
config: RunnableConfig = None,
|
|
45
|
+
**kwargs,
|
|
46
|
+
):
|
|
47
|
+
try:
|
|
48
|
+
context = self.context_from_config(config)
|
|
49
|
+
supabase_url, supabase_key = self.get_supabase_config(context.config)
|
|
50
|
+
|
|
51
|
+
# Create Supabase client
|
|
52
|
+
supabase: Client = create_client(supabase_url, supabase_key)
|
|
53
|
+
|
|
54
|
+
# Prepare function invocation parameters
|
|
55
|
+
invoke_options = {}
|
|
56
|
+
if parameters:
|
|
57
|
+
invoke_options["json"] = parameters
|
|
58
|
+
if headers:
|
|
59
|
+
invoke_options["headers"] = headers
|
|
60
|
+
|
|
61
|
+
# Invoke the Edge Function
|
|
62
|
+
response = supabase.functions.invoke(function_name, invoke_options)
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
"success": True,
|
|
66
|
+
"data": response.json() if hasattr(response, "json") else response,
|
|
67
|
+
"status_code": getattr(response, "status_code", None),
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
except Exception as e:
|
|
71
|
+
logger.error(f"Error invoking Supabase Edge Function: {str(e)}")
|
|
72
|
+
raise ToolException(
|
|
73
|
+
f"Failed to invoke Edge Function '{function_name}': {str(e)}"
|
|
74
|
+
)
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"title": "Supabase",
|
|
5
|
+
"description": "Integration with Supabase backend-as-a-service platform enabling database operations and Edge Function invocations",
|
|
6
|
+
"x-icon": "https://ai.service.crestal.dev/skills/supabase/supabase.png",
|
|
7
|
+
"x-tags": [
|
|
8
|
+
"Database",
|
|
9
|
+
"Backend"
|
|
10
|
+
],
|
|
11
|
+
"properties": {
|
|
12
|
+
"enabled": {
|
|
13
|
+
"type": "boolean",
|
|
14
|
+
"title": "Enabled",
|
|
15
|
+
"description": "Whether this skill is enabled",
|
|
16
|
+
"default": false
|
|
17
|
+
},
|
|
18
|
+
"states": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"properties": {
|
|
21
|
+
"fetch_data": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"title": "Fetch Data",
|
|
24
|
+
"enum": [
|
|
25
|
+
"disabled",
|
|
26
|
+
"public",
|
|
27
|
+
"private"
|
|
28
|
+
],
|
|
29
|
+
"x-enum-title": [
|
|
30
|
+
"Disabled",
|
|
31
|
+
"Agent Owner + All Users",
|
|
32
|
+
"Agent Owner Only"
|
|
33
|
+
],
|
|
34
|
+
"description": "Fetch data from Supabase tables with filtering, ordering, and pagination support",
|
|
35
|
+
"default": "disabled"
|
|
36
|
+
},
|
|
37
|
+
"insert_data": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"title": "Insert Data",
|
|
40
|
+
"enum": [
|
|
41
|
+
"disabled",
|
|
42
|
+
"public",
|
|
43
|
+
"private"
|
|
44
|
+
],
|
|
45
|
+
"x-enum-title": [
|
|
46
|
+
"Disabled",
|
|
47
|
+
"Agent Owner + All Users",
|
|
48
|
+
"Agent Owner Only"
|
|
49
|
+
],
|
|
50
|
+
"description": "Insert new records into Supabase tables",
|
|
51
|
+
"default": "disabled"
|
|
52
|
+
},
|
|
53
|
+
"update_data": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"title": "Update Data",
|
|
56
|
+
"enum": [
|
|
57
|
+
"disabled",
|
|
58
|
+
"public",
|
|
59
|
+
"private"
|
|
60
|
+
],
|
|
61
|
+
"x-enum-title": [
|
|
62
|
+
"Disabled",
|
|
63
|
+
"Agent Owner + All Users",
|
|
64
|
+
"Agent Owner Only"
|
|
65
|
+
],
|
|
66
|
+
"description": "Update existing records in Supabase tables based on filter conditions",
|
|
67
|
+
"default": "disabled"
|
|
68
|
+
},
|
|
69
|
+
"upsert_data": {
|
|
70
|
+
"type": "string",
|
|
71
|
+
"title": "Upsert Data",
|
|
72
|
+
"enum": [
|
|
73
|
+
"disabled",
|
|
74
|
+
"public",
|
|
75
|
+
"private"
|
|
76
|
+
],
|
|
77
|
+
"x-enum-title": [
|
|
78
|
+
"Disabled",
|
|
79
|
+
"Agent Owner + All Users",
|
|
80
|
+
"Agent Owner Only"
|
|
81
|
+
],
|
|
82
|
+
"description": "Insert or update records in Supabase tables based on conflict resolution",
|
|
83
|
+
"default": "disabled"
|
|
84
|
+
},
|
|
85
|
+
"delete_data": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"title": "Delete Data",
|
|
88
|
+
"enum": [
|
|
89
|
+
"disabled",
|
|
90
|
+
"public",
|
|
91
|
+
"private"
|
|
92
|
+
],
|
|
93
|
+
"x-enum-title": [
|
|
94
|
+
"Disabled",
|
|
95
|
+
"Agent Owner + All Users",
|
|
96
|
+
"Agent Owner Only"
|
|
97
|
+
],
|
|
98
|
+
"description": "Delete records from Supabase tables based on filter conditions",
|
|
99
|
+
"default": "disabled"
|
|
100
|
+
},
|
|
101
|
+
"invoke_function": {
|
|
102
|
+
"type": "string",
|
|
103
|
+
"title": "Invoke Edge Function",
|
|
104
|
+
"enum": [
|
|
105
|
+
"disabled",
|
|
106
|
+
"public",
|
|
107
|
+
"private"
|
|
108
|
+
],
|
|
109
|
+
"x-enum-title": [
|
|
110
|
+
"Disabled",
|
|
111
|
+
"Agent Owner + All Users",
|
|
112
|
+
"Agent Owner Only"
|
|
113
|
+
],
|
|
114
|
+
"description": "Invoke Supabase Edge Functions with optional parameters and headers",
|
|
115
|
+
"default": "disabled"
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"description": "States for each Supabase skill"
|
|
119
|
+
},
|
|
120
|
+
"api_key_provider": {
|
|
121
|
+
"type": "string",
|
|
122
|
+
"title": "API Key Provider",
|
|
123
|
+
"description": "Who provides the API key",
|
|
124
|
+
"enum": [
|
|
125
|
+
"agent_owner"
|
|
126
|
+
],
|
|
127
|
+
"x-enum-title": [
|
|
128
|
+
"Owner Provided"
|
|
129
|
+
],
|
|
130
|
+
"default": "agent_owner"
|
|
131
|
+
},
|
|
132
|
+
"supabase_url": {
|
|
133
|
+
"type": "string",
|
|
134
|
+
"title": "Supabase URL",
|
|
135
|
+
"description": "Your Supabase project URL (e.g., https://your-project.supabase.co)",
|
|
136
|
+
"format": "uri"
|
|
137
|
+
},
|
|
138
|
+
"supabase_key": {
|
|
139
|
+
"type": "string",
|
|
140
|
+
"title": "Supabase Anon Key",
|
|
141
|
+
"description": "Your Supabase project's anon/public key",
|
|
142
|
+
"format": "password"
|
|
143
|
+
},
|
|
144
|
+
"public_write_tables": {
|
|
145
|
+
"type": "string",
|
|
146
|
+
"title": "Public Write Tables",
|
|
147
|
+
"description": "Add tables split by comma, when insert,update,upsert,delete enabled in public use, they can only use tables from this list. When skills in owner mode, this list will not limit skills.",
|
|
148
|
+
"default": ""
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"required": [
|
|
152
|
+
"states",
|
|
153
|
+
"enabled"
|
|
154
|
+
],
|
|
155
|
+
"if": {
|
|
156
|
+
"properties": {
|
|
157
|
+
"enabled": {
|
|
158
|
+
"const": true
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"then": {
|
|
163
|
+
"required": [
|
|
164
|
+
"supabase_url",
|
|
165
|
+
"supabase_key"
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<svg width="109" height="113" viewBox="0 0 109 113" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z" fill="url(#paint0_linear)"/>
|
|
3
|
+
<path d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0627L99.1935 40.0627C107.384 40.0627 111.952 49.5228 106.859 55.9374L63.7076 110.284Z" fill="url(#paint1_linear)" fill-opacity="0.2"/>
|
|
4
|
+
<path d="M45.317 2.07103C48.1765 -1.53037 53.9745 0.442937 54.0434 5.041L54.4849 72.2922H9.83113C1.64038 72.2922 -2.92775 62.8321 2.1655 56.4175L45.317 2.07103Z" fill="#3ECF8E"/>
|
|
5
|
+
<defs>
|
|
6
|
+
<linearGradient id="paint0_linear" x1="53.9738" y1="54.974" x2="94.1635" y2="71.8295" gradientUnits="userSpaceOnUse">
|
|
7
|
+
<stop stop-color="#249361"/>
|
|
8
|
+
<stop offset="1" stop-color="#3ECF8E"/>
|
|
9
|
+
</linearGradient>
|
|
10
|
+
<linearGradient id="paint1_linear" x1="36.1558" y1="30.578" x2="54.4844" y2="65.0806" gradientUnits="userSpaceOnUse">
|
|
11
|
+
<stop/>
|
|
12
|
+
<stop offset="1" stop-opacity="0"/>
|
|
13
|
+
</linearGradient>
|
|
14
|
+
</defs>
|
|
15
|
+
</svg>
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Dict, Type
|
|
3
|
+
|
|
4
|
+
from langchain_core.runnables import RunnableConfig
|
|
5
|
+
from langchain_core.tools import ToolException
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
from supabase import Client, create_client
|
|
8
|
+
|
|
9
|
+
from intentkit.skills.supabase.base import SupabaseBaseTool
|
|
10
|
+
|
|
11
|
+
NAME = "supabase_update_data"
|
|
12
|
+
PROMPT = "Update existing data in a Supabase table with filtering conditions."
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SupabaseUpdateDataInput(BaseModel):
|
|
18
|
+
"""Input for SupabaseUpdateData tool."""
|
|
19
|
+
|
|
20
|
+
table: str = Field(description="The name of the table to update data in")
|
|
21
|
+
data: Dict[str, Any] = Field(
|
|
22
|
+
description="The data to update (key-value pairs of columns and new values)"
|
|
23
|
+
)
|
|
24
|
+
filters: Dict[str, Any] = Field(
|
|
25
|
+
description="Dictionary of filters to identify which records to update (e.g., {'id': 123})"
|
|
26
|
+
)
|
|
27
|
+
returning: str = Field(
|
|
28
|
+
default="*", description="Columns to return after update (default: '*' for all)"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SupabaseUpdateData(SupabaseBaseTool):
|
|
33
|
+
"""Tool for updating data in Supabase tables.
|
|
34
|
+
|
|
35
|
+
This tool allows updating records in Supabase tables based on filter conditions.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
name: str = NAME
|
|
39
|
+
description: str = PROMPT
|
|
40
|
+
args_schema: Type[BaseModel] = SupabaseUpdateDataInput
|
|
41
|
+
|
|
42
|
+
async def _arun(
|
|
43
|
+
self,
|
|
44
|
+
table: str,
|
|
45
|
+
data: Dict[str, Any],
|
|
46
|
+
filters: Dict[str, Any],
|
|
47
|
+
returning: str = "*",
|
|
48
|
+
config: RunnableConfig = None,
|
|
49
|
+
**kwargs,
|
|
50
|
+
):
|
|
51
|
+
try:
|
|
52
|
+
context = self.context_from_config(config)
|
|
53
|
+
|
|
54
|
+
# Validate table access for public mode
|
|
55
|
+
self.validate_table_access(table, context)
|
|
56
|
+
|
|
57
|
+
supabase_url, supabase_key = self.get_supabase_config(context.config)
|
|
58
|
+
|
|
59
|
+
# Create Supabase client
|
|
60
|
+
supabase: Client = create_client(supabase_url, supabase_key)
|
|
61
|
+
|
|
62
|
+
# Start building the update query
|
|
63
|
+
query = supabase.table(table).update(data)
|
|
64
|
+
|
|
65
|
+
# Apply filters to identify which records to update
|
|
66
|
+
for column, value in filters.items():
|
|
67
|
+
if isinstance(value, dict):
|
|
68
|
+
# Handle complex filters like {'gte': 18}
|
|
69
|
+
for operator, filter_value in value.items():
|
|
70
|
+
if operator == "eq":
|
|
71
|
+
query = query.eq(column, filter_value)
|
|
72
|
+
elif operator == "neq":
|
|
73
|
+
query = query.neq(column, filter_value)
|
|
74
|
+
elif operator == "gt":
|
|
75
|
+
query = query.gt(column, filter_value)
|
|
76
|
+
elif operator == "gte":
|
|
77
|
+
query = query.gte(column, filter_value)
|
|
78
|
+
elif operator == "lt":
|
|
79
|
+
query = query.lt(column, filter_value)
|
|
80
|
+
elif operator == "lte":
|
|
81
|
+
query = query.lte(column, filter_value)
|
|
82
|
+
elif operator == "like":
|
|
83
|
+
query = query.like(column, filter_value)
|
|
84
|
+
elif operator == "ilike":
|
|
85
|
+
query = query.ilike(column, filter_value)
|
|
86
|
+
elif operator == "in":
|
|
87
|
+
query = query.in_(column, filter_value)
|
|
88
|
+
else:
|
|
89
|
+
logger.warning(f"Unknown filter operator: {operator}")
|
|
90
|
+
else:
|
|
91
|
+
# Simple equality filter
|
|
92
|
+
query = query.eq(column, value)
|
|
93
|
+
|
|
94
|
+
# Execute the update
|
|
95
|
+
response = query.execute()
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
"success": True,
|
|
99
|
+
"data": response.data,
|
|
100
|
+
"count": len(response.data) if response.data else 0,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
except Exception as e:
|
|
104
|
+
logger.error(f"Error updating data in Supabase: {str(e)}")
|
|
105
|
+
raise ToolException(f"Failed to update data in table '{table}': {str(e)}")
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Dict, List, Type, Union
|
|
3
|
+
|
|
4
|
+
from langchain_core.runnables import RunnableConfig
|
|
5
|
+
from langchain_core.tools import ToolException
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
from supabase import Client, create_client
|
|
8
|
+
|
|
9
|
+
from intentkit.skills.supabase.base import SupabaseBaseTool
|
|
10
|
+
|
|
11
|
+
NAME = "supabase_upsert_data"
|
|
12
|
+
PROMPT = (
|
|
13
|
+
"Upsert (insert or update) data in a Supabase table based on conflict resolution."
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SupabaseUpsertDataInput(BaseModel):
|
|
20
|
+
"""Input for SupabaseUpsertData tool."""
|
|
21
|
+
|
|
22
|
+
table: str = Field(description="The name of the table to upsert data into")
|
|
23
|
+
data: Union[Dict[str, Any], List[Dict[str, Any]]] = Field(
|
|
24
|
+
description="The data to upsert. Can be a single object or a list of objects"
|
|
25
|
+
)
|
|
26
|
+
on_conflict: str = Field(
|
|
27
|
+
description="The column(s) to use for conflict resolution (e.g., 'id' or 'email,username')"
|
|
28
|
+
)
|
|
29
|
+
returning: str = Field(
|
|
30
|
+
default="*", description="Columns to return after upsert (default: '*' for all)"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class SupabaseUpsertData(SupabaseBaseTool):
|
|
35
|
+
"""Tool for upserting data in Supabase tables.
|
|
36
|
+
|
|
37
|
+
This tool allows inserting new records or updating existing ones based on conflict resolution.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
name: str = NAME
|
|
41
|
+
description: str = PROMPT
|
|
42
|
+
args_schema: Type[BaseModel] = SupabaseUpsertDataInput
|
|
43
|
+
|
|
44
|
+
async def _arun(
|
|
45
|
+
self,
|
|
46
|
+
table: str,
|
|
47
|
+
data: Union[Dict[str, Any], List[Dict[str, Any]]],
|
|
48
|
+
on_conflict: str,
|
|
49
|
+
returning: str = "*",
|
|
50
|
+
config: RunnableConfig = None,
|
|
51
|
+
**kwargs,
|
|
52
|
+
):
|
|
53
|
+
try:
|
|
54
|
+
context = self.context_from_config(config)
|
|
55
|
+
|
|
56
|
+
# Validate table access for public mode
|
|
57
|
+
self.validate_table_access(table, context)
|
|
58
|
+
|
|
59
|
+
supabase_url, supabase_key = self.get_supabase_config(context.config)
|
|
60
|
+
|
|
61
|
+
# Create Supabase client
|
|
62
|
+
supabase: Client = create_client(supabase_url, supabase_key)
|
|
63
|
+
|
|
64
|
+
# Upsert data
|
|
65
|
+
response = (
|
|
66
|
+
supabase.table(table).upsert(data, on_conflict=on_conflict).execute()
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
"success": True,
|
|
71
|
+
"data": response.data,
|
|
72
|
+
"count": len(response.data) if response.data else 0,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.error(f"Error upserting data in Supabase: {str(e)}")
|
|
77
|
+
raise ToolException(f"Failed to upsert data in table '{table}': {str(e)}")
|
|
@@ -40,7 +40,7 @@ class ReadAgentApiKey(SystemBaseTool):
|
|
|
40
40
|
"""Retrieve or generate an API key for the agent."""
|
|
41
41
|
# Get context from runnable config to access agent.id
|
|
42
42
|
context = self.context_from_config(config)
|
|
43
|
-
agent_id = context.
|
|
43
|
+
agent_id = context.agent_id
|
|
44
44
|
|
|
45
45
|
# Get agent data from skill store
|
|
46
46
|
agent_data = await self.skill_store.get_agent_data(agent_id)
|
|
@@ -44,7 +44,7 @@ class RegenerateAgentApiKey(SystemBaseTool):
|
|
|
44
44
|
"""Generate and set a new API key for the agent."""
|
|
45
45
|
# Get context from runnable config to access agent.id
|
|
46
46
|
context = self.context_from_config(config)
|
|
47
|
-
agent_id = context.
|
|
47
|
+
agent_id = context.agent_id
|
|
48
48
|
|
|
49
49
|
# Get agent data from skill store
|
|
50
50
|
agent_data = await self.skill_store.get_agent_data(agent_id)
|