Spaces:
Sleeping
Sleeping
Commit Β·
ed33951
1
Parent(s): 2d6f17a
Fix lazy token loading in tools, add github_list_commits tool, load_dotenv in main
Browse files
main.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
| 1 |
import logging
|
|
|
|
|
|
|
| 2 |
|
| 3 |
from contextlib import asynccontextmanager
|
| 4 |
|
|
|
|
| 1 |
import logging
|
| 2 |
+
from dotenv import load_dotenv
|
| 3 |
+
load_dotenv()
|
| 4 |
|
| 5 |
from contextlib import asynccontextmanager
|
| 6 |
|
toolsforgitnotionslack/tools/github_tools.py
CHANGED
|
@@ -4,21 +4,19 @@ GitHub tools β discovery-first, markdown-only reads, paginated, rate-limit awa
|
|
| 4 |
import os
|
| 5 |
import httpx
|
| 6 |
|
| 7 |
-
GH
|
| 8 |
-
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "")
|
| 9 |
-
GITHUB_REPO = os.environ.get("GITHUB_REPO", "")
|
| 10 |
|
| 11 |
|
| 12 |
def _headers() -> dict:
|
| 13 |
return {
|
| 14 |
-
"Authorization": f"Bearer {GITHUB_TOKEN}",
|
| 15 |
"Accept": "application/vnd.github+json",
|
| 16 |
"X-GitHub-Api-Version": "2022-11-28",
|
| 17 |
}
|
| 18 |
|
| 19 |
|
| 20 |
def _target(repo: str) -> str:
|
| 21 |
-
return repo or GITHUB_REPO
|
| 22 |
|
| 23 |
|
| 24 |
def _rate_warn(resp: httpx.Response) -> str | None:
|
|
@@ -170,6 +168,31 @@ async def github_list_markdown_files(repo: str = "", folder: str = "") -> str:
|
|
| 170 |
return f"[{t}] {len(items)} markdown file(s):\n" + "\n".join(f"β’ {i['path']}" for i in items)
|
| 171 |
|
| 172 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
# ββ Registry βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 174 |
|
| 175 |
GITHUB_TOOL_FNS = {
|
|
@@ -179,6 +202,7 @@ GITHUB_TOOL_FNS = {
|
|
| 179 |
"github_read_file": github_read_file,
|
| 180 |
"github_search_code": github_search_code,
|
| 181 |
"github_list_markdown_files": github_list_markdown_files,
|
|
|
|
| 182 |
}
|
| 183 |
|
| 184 |
GITHUB_TOOLS = [
|
|
@@ -253,4 +277,17 @@ GITHUB_TOOLS = [
|
|
| 253 |
"repo": {"type": "string"}, "folder": {"type": "string"},
|
| 254 |
}},
|
| 255 |
}},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 256 |
]
|
|
|
|
| 4 |
import os
|
| 5 |
import httpx
|
| 6 |
|
| 7 |
+
GH = "https://api.github.com"
|
|
|
|
|
|
|
| 8 |
|
| 9 |
|
| 10 |
def _headers() -> dict:
|
| 11 |
return {
|
| 12 |
+
"Authorization": f"Bearer {os.environ.get('GITHUB_TOKEN', '')}",
|
| 13 |
"Accept": "application/vnd.github+json",
|
| 14 |
"X-GitHub-Api-Version": "2022-11-28",
|
| 15 |
}
|
| 16 |
|
| 17 |
|
| 18 |
def _target(repo: str) -> str:
|
| 19 |
+
return repo or os.environ.get("GITHUB_REPO", "")
|
| 20 |
|
| 21 |
|
| 22 |
def _rate_warn(resp: httpx.Response) -> str | None:
|
|
|
|
| 168 |
return f"[{t}] {len(items)} markdown file(s):\n" + "\n".join(f"β’ {i['path']}" for i in items)
|
| 169 |
|
| 170 |
|
| 171 |
+
async def github_list_commits(repo: str = "", branch: str = "", page: int = 1) -> str:
|
| 172 |
+
t = _target(repo)
|
| 173 |
+
if not t: return "Provide repo as 'owner/repo' or set GITHUB_REPO."
|
| 174 |
+
params = {"per_page": 20, "page": page}
|
| 175 |
+
if branch:
|
| 176 |
+
params["sha"] = branch
|
| 177 |
+
async with httpx.AsyncClient(timeout=15) as h:
|
| 178 |
+
r = await h.get(f"{GH}/repos/{t}/commits", headers=_headers(), params=params)
|
| 179 |
+
if w := _rate_warn(r): return w
|
| 180 |
+
if r.status_code == 404: return f"Repo not found: {t}"
|
| 181 |
+
if r.status_code != 200: return f"GitHub error {r.status_code}: {r.text[:200]}"
|
| 182 |
+
commits = r.json()
|
| 183 |
+
if not commits: return "No commits found."
|
| 184 |
+
lines = []
|
| 185 |
+
for c in commits:
|
| 186 |
+
sha = c["sha"][:7]
|
| 187 |
+
msg = c["commit"]["message"].split("\n")[0][:80]
|
| 188 |
+
author = c["commit"]["author"]["name"]
|
| 189 |
+
date = c["commit"]["author"]["date"][:10]
|
| 190 |
+
lines.append(f"β’ {sha} {date} {author}: {msg}")
|
| 191 |
+
if len(commits) == 20:
|
| 192 |
+
lines.append(f"[Page {page} β call with page={page+1} for more]")
|
| 193 |
+
return "\n".join(lines)
|
| 194 |
+
|
| 195 |
+
|
| 196 |
# ββ Registry βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 197 |
|
| 198 |
GITHUB_TOOL_FNS = {
|
|
|
|
| 202 |
"github_read_file": github_read_file,
|
| 203 |
"github_search_code": github_search_code,
|
| 204 |
"github_list_markdown_files": github_list_markdown_files,
|
| 205 |
+
"github_list_commits": github_list_commits,
|
| 206 |
}
|
| 207 |
|
| 208 |
GITHUB_TOOLS = [
|
|
|
|
| 277 |
"repo": {"type": "string"}, "folder": {"type": "string"},
|
| 278 |
}},
|
| 279 |
}},
|
| 280 |
+
{"type": "function", "function": {
|
| 281 |
+
"name": "github_list_commits",
|
| 282 |
+
"description": (
|
| 283 |
+
"List recent commits in a GitHub repo. "
|
| 284 |
+
"Use this when asked about recent changes, commit history, or who changed what. "
|
| 285 |
+
"Returns SHA, date, author, and commit message for each commit."
|
| 286 |
+
),
|
| 287 |
+
"parameters": {"type": "object", "properties": {
|
| 288 |
+
"repo": {"type": "string", "description": "owner/repo"},
|
| 289 |
+
"branch": {"type": "string", "description": "Branch name, default is the repo default branch"},
|
| 290 |
+
"page": {"type": "integer", "description": "Page number, default 1"},
|
| 291 |
+
}},
|
| 292 |
+
}},
|
| 293 |
]
|
toolsforgitnotionslack/tools/notion_tools.py
CHANGED
|
@@ -3,15 +3,12 @@ Notion tools β search, read pages, list and query databases.
|
|
| 3 |
"""
|
| 4 |
import os
|
| 5 |
import httpx
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
"Notion-Version": "2022-06-28",
|
| 13 |
-
"Content-Type": "application/json",
|
| 14 |
-
}
|
| 15 |
|
| 16 |
|
| 17 |
def _extract_rich_text(rt: list) -> str:
|
|
@@ -24,7 +21,7 @@ async def notion_search(query: str, filter_type: str = "") -> str:
|
|
| 24 |
body["filter"] = {"value": filter_type, "property": "object"}
|
| 25 |
async with httpx.AsyncClient(timeout=15) as h:
|
| 26 |
r = await h.post("https://api.notion.com/v1/search",
|
| 27 |
-
headers=
|
| 28 |
if r.status_code != 200: return f"Notion error {r.status_code}: {r.text[:300]}"
|
| 29 |
results = r.json().get("results", [])
|
| 30 |
if not results: return "No Notion pages or databases found."
|
|
@@ -49,7 +46,7 @@ async def notion_read_page(page_id: str, max_blocks: int = 80) -> str:
|
|
| 49 |
async with httpx.AsyncClient(timeout=15) as h:
|
| 50 |
r = await h.get(
|
| 51 |
f"https://api.notion.com/v1/blocks/{page_id}/children",
|
| 52 |
-
headers=
|
| 53 |
params={"page_size": max_blocks},
|
| 54 |
)
|
| 55 |
if r.status_code != 200: return f"Notion error {r.status_code}: {r.text[:300]}"
|
|
@@ -91,7 +88,7 @@ async def notion_list_databases(query: str = "") -> str:
|
|
| 91 |
if query: body["query"] = query
|
| 92 |
async with httpx.AsyncClient(timeout=15) as h:
|
| 93 |
r = await h.post("https://api.notion.com/v1/search",
|
| 94 |
-
headers=
|
| 95 |
if r.status_code != 200: return f"Notion error {r.status_code}: {r.text[:300]}"
|
| 96 |
results = r.json().get("results", [])
|
| 97 |
if not results: return "No databases found."
|
|
@@ -112,7 +109,7 @@ async def notion_query_database(database_id: str, filter_property: str = "",
|
|
| 112 |
async with httpx.AsyncClient(timeout=15) as h:
|
| 113 |
r = await h.post(
|
| 114 |
f"https://api.notion.com/v1/databases/{database_id}/query",
|
| 115 |
-
headers=
|
| 116 |
)
|
| 117 |
if r.status_code != 200: return f"Notion error {r.status_code}: {r.text[:300]}"
|
| 118 |
results = r.json().get("results", [])
|
|
|
|
| 3 |
"""
|
| 4 |
import os
|
| 5 |
import httpx
|
| 6 |
+
def _notion_headers() -> dict:
|
| 7 |
+
return {
|
| 8 |
+
"Authorization": f"Bearer {os.environ.get('NOTION_API_TOKEN', '')}",
|
| 9 |
+
"Notion-Version": "2022-06-28",
|
| 10 |
+
"Content-Type": "application/json",
|
| 11 |
+
}
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
|
| 14 |
def _extract_rich_text(rt: list) -> str:
|
|
|
|
| 21 |
body["filter"] = {"value": filter_type, "property": "object"}
|
| 22 |
async with httpx.AsyncClient(timeout=15) as h:
|
| 23 |
r = await h.post("https://api.notion.com/v1/search",
|
| 24 |
+
headers=_notion_headers(), json=body)
|
| 25 |
if r.status_code != 200: return f"Notion error {r.status_code}: {r.text[:300]}"
|
| 26 |
results = r.json().get("results", [])
|
| 27 |
if not results: return "No Notion pages or databases found."
|
|
|
|
| 46 |
async with httpx.AsyncClient(timeout=15) as h:
|
| 47 |
r = await h.get(
|
| 48 |
f"https://api.notion.com/v1/blocks/{page_id}/children",
|
| 49 |
+
headers=_notion_headers(),
|
| 50 |
params={"page_size": max_blocks},
|
| 51 |
)
|
| 52 |
if r.status_code != 200: return f"Notion error {r.status_code}: {r.text[:300]}"
|
|
|
|
| 88 |
if query: body["query"] = query
|
| 89 |
async with httpx.AsyncClient(timeout=15) as h:
|
| 90 |
r = await h.post("https://api.notion.com/v1/search",
|
| 91 |
+
headers=_notion_headers(), json=body)
|
| 92 |
if r.status_code != 200: return f"Notion error {r.status_code}: {r.text[:300]}"
|
| 93 |
results = r.json().get("results", [])
|
| 94 |
if not results: return "No databases found."
|
|
|
|
| 109 |
async with httpx.AsyncClient(timeout=15) as h:
|
| 110 |
r = await h.post(
|
| 111 |
f"https://api.notion.com/v1/databases/{database_id}/query",
|
| 112 |
+
headers=_notion_headers(), json=body,
|
| 113 |
)
|
| 114 |
if r.status_code != 200: return f"Notion error {r.status_code}: {r.text[:300]}"
|
| 115 |
results = r.json().get("results", [])
|
toolsforgitnotionslack/tools/slack_tools.py
CHANGED
|
@@ -4,11 +4,8 @@ Slack tools β search, history, threads, channel metadata.
|
|
| 4 |
import os
|
| 5 |
import httpx
|
| 6 |
|
| 7 |
-
SLACK_TOKEN = os.environ.get("SLACK_BOT_TOKEN", "")
|
| 8 |
-
|
| 9 |
-
|
| 10 |
def _headers() -> dict:
|
| 11 |
-
return {"Authorization": f"Bearer {
|
| 12 |
|
| 13 |
|
| 14 |
async def slack_list_channels(filter_name: str = "") -> str:
|
|
|
|
| 4 |
import os
|
| 5 |
import httpx
|
| 6 |
|
|
|
|
|
|
|
|
|
|
| 7 |
def _headers() -> dict:
|
| 8 |
+
return {"Authorization": f"Bearer {os.environ.get('SLACK_BOT_TOKEN', '')}"}
|
| 9 |
|
| 10 |
|
| 11 |
async def slack_list_channels(filter_name: str = "") -> str:
|