Update app.py
Browse files
app.py
CHANGED
|
@@ -66,7 +66,7 @@ OAUTH_CONFIGS = {
|
|
| 66 |
"client_secret": "ec8548ef297ed59d8372e5bc549d8ed9adc996f7",
|
| 67 |
"auth_url": "https://github.com/login/oauth/authorize",
|
| 68 |
"token_url": "https://github.com/login/oauth/access_token",
|
| 69 |
-
"scope": "read:user repo"
|
| 70 |
},
|
| 71 |
"figma": {
|
| 72 |
"client_id": "Cg79RWBHaeyW2afGJVSQi8",
|
|
@@ -137,11 +137,12 @@ STRICT IDENTITY RULES:
|
|
| 137 |
- To Delete/Remove a page: Search for the page_id, then call `notion_delete_page`. NEVER tell the user to delete manually.
|
| 138 |
- To Create a new page: Use `notion_create_page`.
|
| 139 |
|
| 140 |
-
4. GITHUB CONNECTOR WORKFLOW RULES:
|
| 141 |
-
-
|
| 142 |
-
-
|
| 143 |
-
-
|
| 144 |
-
-
|
|
|
|
| 145 |
|
| 146 |
5. NATURAL CONVERSATION & TOOL RESPONSE STYLE:
|
| 147 |
- Do NOT introduce your company, founder, or model name in general responses unless requested.
|
|
@@ -495,17 +496,173 @@ TOOLS = [
|
|
| 495 |
"properties": {
|
| 496 |
"owner": {"type": "string", "description": "Repository owner / username"},
|
| 497 |
"repo": {"type": "string", "description": "Repository name"},
|
| 498 |
-
"path": {"type": "string", "description": "Path to file or folder, default empty for root"}
|
|
|
|
| 499 |
},
|
| 500 |
"required": ["owner", "repo"]
|
| 501 |
}
|
| 502 |
}
|
| 503 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 504 |
{
|
| 505 |
"type": "function",
|
| 506 |
"function": {
|
| 507 |
"name": "github_list_issues",
|
| 508 |
-
"description": "List issues
|
| 509 |
"parameters": {
|
| 510 |
"type": "object",
|
| 511 |
"properties": {
|
|
@@ -533,6 +690,71 @@ TOOLS = [
|
|
| 533 |
"required": ["owner", "repo", "title"]
|
| 534 |
}
|
| 535 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 536 |
}
|
| 537 |
]
|
| 538 |
|
|
@@ -665,12 +887,14 @@ async def github_list_repos(user_id: str, max_results: int = 10):
|
|
| 665 |
except Exception as e:
|
| 666 |
return f"Error listing GitHub repos: {str(e)}"
|
| 667 |
|
| 668 |
-
async def github_get_repo_content(user_id: str, owner: str, repo: str, path: str = ""):
|
| 669 |
headers = await github_get_headers(user_id)
|
| 670 |
if not headers:
|
| 671 |
return "GitHub is not connected."
|
| 672 |
try:
|
| 673 |
url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}"
|
|
|
|
|
|
|
| 674 |
async with httpx.AsyncClient() as c:
|
| 675 |
res = await c.get(url, headers=headers)
|
| 676 |
if res.status_code != 200:
|
|
@@ -685,11 +909,185 @@ async def github_get_repo_content(user_id: str, owner: str, repo: str, path: str
|
|
| 685 |
if data.get("encoding") == "base64" and data.get("content"):
|
| 686 |
content = base64.b64decode(data.get("content")).decode("utf-8", errors="ignore")
|
| 687 |
return content
|
| 688 |
-
return f"File Name: {data.get('name')} | Size: {data.get('size')} bytes"
|
| 689 |
return "Unable to parse content."
|
| 690 |
except Exception as e:
|
| 691 |
return f"Error reading GitHub content: {str(e)}"
|
| 692 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 693 |
async def github_list_issues(user_id: str, owner: str, repo: str, state: str = "open"):
|
| 694 |
headers = await github_get_headers(user_id)
|
| 695 |
if not headers:
|
|
@@ -726,6 +1124,79 @@ async def github_create_issue(user_id: str, owner: str, repo: str, title: str, b
|
|
| 726 |
except Exception as e:
|
| 727 |
return f"Error creating GitHub issue: {str(e)}"
|
| 728 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 729 |
async def notion_get_headers(user_id: str):
|
| 730 |
token_data = await get_token_from_supabase(user_id, "notion")
|
| 731 |
if not token_data:
|
|
@@ -1231,11 +1702,37 @@ async def execute_tool_call(user_id: str, func_name: str, args: dict) -> str:
|
|
| 1231 |
elif func_name == "github_list_repos":
|
| 1232 |
return await github_list_repos(user_id, args.get("max_results", 10))
|
| 1233 |
elif func_name == "github_get_repo_content":
|
| 1234 |
-
return await github_get_repo_content(user_id, args.get("owner", ""), args.get("repo", ""), args.get("path", ""))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1235 |
elif func_name == "github_list_issues":
|
| 1236 |
return await github_list_issues(user_id, args.get("owner", ""), args.get("repo", ""), args.get("state", "open"))
|
| 1237 |
elif func_name == "github_create_issue":
|
| 1238 |
return await github_create_issue(user_id, args.get("owner", ""), args.get("repo", ""), args.get("title", ""), args.get("body", ""))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1239 |
return "Unknown tool function."
|
| 1240 |
|
| 1241 |
def count_tokens(messages):
|
|
|
|
| 66 |
"client_secret": "ec8548ef297ed59d8372e5bc549d8ed9adc996f7",
|
| 67 |
"auth_url": "https://github.com/login/oauth/authorize",
|
| 68 |
"token_url": "https://github.com/login/oauth/access_token",
|
| 69 |
+
"scope": "read:user repo workflow"
|
| 70 |
},
|
| 71 |
"figma": {
|
| 72 |
"client_id": "Cg79RWBHaeyW2afGJVSQi8",
|
|
|
|
| 137 |
- To Delete/Remove a page: Search for the page_id, then call `notion_delete_page`. NEVER tell the user to delete manually.
|
| 138 |
- To Create a new page: Use `notion_create_page`.
|
| 139 |
|
| 140 |
+
4. GITHUB CONNECTOR WORKFLOW RULES (FULL CAPABILITY):
|
| 141 |
+
- READ & SEARCH: Use `github_get_repo_content` to read files/folders, `github_search_code` to search code, `github_list_commits` for history.
|
| 142 |
+
- EDIT & WRITE: Use `github_create_or_update_file` to create or edit files, `github_delete_file` to remove files.
|
| 143 |
+
- BRANCHES & PRS: Use `github_create_branch`, `github_delete_branch`, `github_list_pull_requests`, `github_get_pull_request`, `github_create_pull_request`, `github_merge_pull_request`.
|
| 144 |
+
- ISSUES & COMMENTS: Use `github_list_issues`, `github_create_issue`, `github_update_issue` (to edit/close/reopen), `github_create_comment`.
|
| 145 |
+
- AUTOMATION: Use `github_list_workflow_runs` to check CI/CD build status.
|
| 146 |
|
| 147 |
5. NATURAL CONVERSATION & TOOL RESPONSE STYLE:
|
| 148 |
- Do NOT introduce your company, founder, or model name in general responses unless requested.
|
|
|
|
| 496 |
"properties": {
|
| 497 |
"owner": {"type": "string", "description": "Repository owner / username"},
|
| 498 |
"repo": {"type": "string", "description": "Repository name"},
|
| 499 |
+
"path": {"type": "string", "description": "Path to file or folder, default empty for root"},
|
| 500 |
+
"branch": {"type": "string", "description": "Branch name, default main/master"}
|
| 501 |
},
|
| 502 |
"required": ["owner", "repo"]
|
| 503 |
}
|
| 504 |
}
|
| 505 |
},
|
| 506 |
+
{
|
| 507 |
+
"type": "function",
|
| 508 |
+
"function": {
|
| 509 |
+
"name": "github_create_or_update_file",
|
| 510 |
+
"description": "Create a new file or edit/update an existing file in GitHub repository",
|
| 511 |
+
"parameters": {
|
| 512 |
+
"type": "object",
|
| 513 |
+
"properties": {
|
| 514 |
+
"owner": {"type": "string", "description": "Repository owner"},
|
| 515 |
+
"repo": {"type": "string", "description": "Repository name"},
|
| 516 |
+
"path": {"type": "string", "description": "File path in repository e.g. 'src/App.kt'"},
|
| 517 |
+
"content": {"type": "string", "description": "Text content for the file"},
|
| 518 |
+
"message": {"type": "string", "description": "Commit message"},
|
| 519 |
+
"branch": {"type": "string", "description": "Target branch name"}
|
| 520 |
+
},
|
| 521 |
+
"required": ["owner", "repo", "path", "content", "message"]
|
| 522 |
+
}
|
| 523 |
+
}
|
| 524 |
+
},
|
| 525 |
+
{
|
| 526 |
+
"type": "function",
|
| 527 |
+
"function": {
|
| 528 |
+
"name": "github_delete_file",
|
| 529 |
+
"description": "Delete a file from GitHub repository",
|
| 530 |
+
"parameters": {
|
| 531 |
+
"type": "object",
|
| 532 |
+
"properties": {
|
| 533 |
+
"owner": {"type": "string", "description": "Repository owner"},
|
| 534 |
+
"repo": {"type": "string", "description": "Repository name"},
|
| 535 |
+
"path": {"type": "string", "description": "File path to delete"},
|
| 536 |
+
"message": {"type": "string", "description": "Commit message"},
|
| 537 |
+
"branch": {"type": "string", "description": "Branch name"}
|
| 538 |
+
},
|
| 539 |
+
"required": ["owner", "repo", "path", "message"]
|
| 540 |
+
}
|
| 541 |
+
}
|
| 542 |
+
},
|
| 543 |
+
{
|
| 544 |
+
"type": "function",
|
| 545 |
+
"function": {
|
| 546 |
+
"name": "github_create_branch",
|
| 547 |
+
"description": "Create a new Git branch in GitHub repository",
|
| 548 |
+
"parameters": {
|
| 549 |
+
"type": "object",
|
| 550 |
+
"properties": {
|
| 551 |
+
"owner": {"type": "string", "description": "Repository owner"},
|
| 552 |
+
"repo": {"type": "string", "description": "Repository name"},
|
| 553 |
+
"new_branch": {"type": "string", "description": "Name of new branch"},
|
| 554 |
+
"base_branch": {"type": "string", "description": "Source branch to copy from, default 'main'"}
|
| 555 |
+
},
|
| 556 |
+
"required": ["owner", "repo", "new_branch"]
|
| 557 |
+
}
|
| 558 |
+
}
|
| 559 |
+
},
|
| 560 |
+
{
|
| 561 |
+
"type": "function",
|
| 562 |
+
"function": {
|
| 563 |
+
"name": "github_delete_branch",
|
| 564 |
+
"description": "Delete a branch from GitHub repository",
|
| 565 |
+
"parameters": {
|
| 566 |
+
"type": "object",
|
| 567 |
+
"properties": {
|
| 568 |
+
"owner": {"type": "string", "description": "Repository owner"},
|
| 569 |
+
"repo": {"type": "string", "description": "Repository name"},
|
| 570 |
+
"branch": {"type": "string", "description": "Branch name to delete"}
|
| 571 |
+
},
|
| 572 |
+
"required": ["owner", "repo", "branch"]
|
| 573 |
+
}
|
| 574 |
+
}
|
| 575 |
+
},
|
| 576 |
+
{
|
| 577 |
+
"type": "function",
|
| 578 |
+
"function": {
|
| 579 |
+
"name": "github_list_commits",
|
| 580 |
+
"description": "List commit history for a GitHub repository or specific file path",
|
| 581 |
+
"parameters": {
|
| 582 |
+
"type": "object",
|
| 583 |
+
"properties": {
|
| 584 |
+
"owner": {"type": "string", "description": "Repository owner"},
|
| 585 |
+
"repo": {"type": "string", "description": "Repository name"},
|
| 586 |
+
"path": {"type": "string", "description": "Optional file path filter"},
|
| 587 |
+
"max_results": {"type": "integer", "description": "Number of commits, default 10"}
|
| 588 |
+
},
|
| 589 |
+
"required": ["owner", "repo"]
|
| 590 |
+
}
|
| 591 |
+
}
|
| 592 |
+
},
|
| 593 |
+
{
|
| 594 |
+
"type": "function",
|
| 595 |
+
"function": {
|
| 596 |
+
"name": "github_list_pull_requests",
|
| 597 |
+
"description": "List Pull Requests (PRs) in a GitHub repository",
|
| 598 |
+
"parameters": {
|
| 599 |
+
"type": "object",
|
| 600 |
+
"properties": {
|
| 601 |
+
"owner": {"type": "string", "description": "Repository owner"},
|
| 602 |
+
"repo": {"type": "string", "description": "Repository name"},
|
| 603 |
+
"state": {"type": "string", "description": "State e.g. 'open', 'closed', 'all'"}
|
| 604 |
+
},
|
| 605 |
+
"required": ["owner", "repo"]
|
| 606 |
+
}
|
| 607 |
+
}
|
| 608 |
+
},
|
| 609 |
+
{
|
| 610 |
+
"type": "function",
|
| 611 |
+
"function": {
|
| 612 |
+
"name": "github_get_pull_request",
|
| 613 |
+
"description": "Get detailed information and diff summary of a Pull Request",
|
| 614 |
+
"parameters": {
|
| 615 |
+
"type": "object",
|
| 616 |
+
"properties": {
|
| 617 |
+
"owner": {"type": "string", "description": "Repository owner"},
|
| 618 |
+
"repo": {"type": "string", "description": "Repository name"},
|
| 619 |
+
"pull_number": {"type": "integer", "description": "PR number"}
|
| 620 |
+
},
|
| 621 |
+
"required": ["owner", "repo", "pull_number"]
|
| 622 |
+
}
|
| 623 |
+
}
|
| 624 |
+
},
|
| 625 |
+
{
|
| 626 |
+
"type": "function",
|
| 627 |
+
"function": {
|
| 628 |
+
"name": "github_create_pull_request",
|
| 629 |
+
"description": "Create a new Pull Request in GitHub repository",
|
| 630 |
+
"parameters": {
|
| 631 |
+
"type": "object",
|
| 632 |
+
"properties": {
|
| 633 |
+
"owner": {"type": "string", "description": "Repository owner"},
|
| 634 |
+
"repo": {"type": "string", "description": "Repository name"},
|
| 635 |
+
"title": {"type": "string", "description": "PR title"},
|
| 636 |
+
"head": {"type": "string", "description": "Branch containing new code"},
|
| 637 |
+
"base": {"type": "string", "description": "Target branch to merge into e.g. 'main'"},
|
| 638 |
+
"body": {"type": "string", "description": "PR description"}
|
| 639 |
+
},
|
| 640 |
+
"required": ["owner", "repo", "title", "head", "base"]
|
| 641 |
+
}
|
| 642 |
+
}
|
| 643 |
+
},
|
| 644 |
+
{
|
| 645 |
+
"type": "function",
|
| 646 |
+
"function": {
|
| 647 |
+
"name": "github_merge_pull_request",
|
| 648 |
+
"description": "Merge a Pull Request in GitHub repository",
|
| 649 |
+
"parameters": {
|
| 650 |
+
"type": "object",
|
| 651 |
+
"properties": {
|
| 652 |
+
"owner": {"type": "string", "description": "Repository owner"},
|
| 653 |
+
"repo": {"type": "string", "description": "Repository name"},
|
| 654 |
+
"pull_number": {"type": "integer", "description": "PR number"},
|
| 655 |
+
"commit_title": {"type": "string", "description": "Optional merge commit message"}
|
| 656 |
+
},
|
| 657 |
+
"required": ["owner", "repo", "pull_number"]
|
| 658 |
+
}
|
| 659 |
+
}
|
| 660 |
+
},
|
| 661 |
{
|
| 662 |
"type": "function",
|
| 663 |
"function": {
|
| 664 |
"name": "github_list_issues",
|
| 665 |
+
"description": "List issues in a GitHub repository",
|
| 666 |
"parameters": {
|
| 667 |
"type": "object",
|
| 668 |
"properties": {
|
|
|
|
| 690 |
"required": ["owner", "repo", "title"]
|
| 691 |
}
|
| 692 |
}
|
| 693 |
+
},
|
| 694 |
+
{
|
| 695 |
+
"type": "function",
|
| 696 |
+
"function": {
|
| 697 |
+
"name": "github_update_issue",
|
| 698 |
+
"description": "Update or close/reopen an existing issue or Pull Request in GitHub",
|
| 699 |
+
"parameters": {
|
| 700 |
+
"type": "object",
|
| 701 |
+
"properties": {
|
| 702 |
+
"owner": {"type": "string", "description": "Repository owner"},
|
| 703 |
+
"repo": {"type": "string", "description": "Repository name"},
|
| 704 |
+
"issue_number": {"type": "integer", "description": "Issue number"},
|
| 705 |
+
"title": {"type": "string", "description": "New title"},
|
| 706 |
+
"body": {"type": "string", "description": "New body content"},
|
| 707 |
+
"state": {"type": "string", "description": "State 'open' or 'closed'"}
|
| 708 |
+
},
|
| 709 |
+
"required": ["owner", "repo", "issue_number"]
|
| 710 |
+
}
|
| 711 |
+
}
|
| 712 |
+
},
|
| 713 |
+
{
|
| 714 |
+
"type": "function",
|
| 715 |
+
"function": {
|
| 716 |
+
"name": "github_create_comment",
|
| 717 |
+
"description": "Add a comment to an issue or Pull Request in GitHub",
|
| 718 |
+
"parameters": {
|
| 719 |
+
"type": "object",
|
| 720 |
+
"properties": {
|
| 721 |
+
"owner": {"type": "string", "description": "Repository owner"},
|
| 722 |
+
"repo": {"type": "string", "description": "Repository name"},
|
| 723 |
+
"issue_number": {"type": "integer", "description": "Issue or PR number"},
|
| 724 |
+
"body": {"type": "string", "description": "Comment text"}
|
| 725 |
+
},
|
| 726 |
+
"required": ["owner", "repo", "issue_number", "body"]
|
| 727 |
+
}
|
| 728 |
+
}
|
| 729 |
+
},
|
| 730 |
+
{
|
| 731 |
+
"type": "function",
|
| 732 |
+
"function": {
|
| 733 |
+
"name": "github_search_code",
|
| 734 |
+
"description": "Search code across user repositories on GitHub",
|
| 735 |
+
"parameters": {
|
| 736 |
+
"type": "object",
|
| 737 |
+
"properties": {
|
| 738 |
+
"query": {"type": "string", "description": "Search text or function name e.g. 'function_name user:owner'"}
|
| 739 |
+
},
|
| 740 |
+
"required": ["query"]
|
| 741 |
+
}
|
| 742 |
+
}
|
| 743 |
+
},
|
| 744 |
+
{
|
| 745 |
+
"type": "function",
|
| 746 |
+
"function": {
|
| 747 |
+
"name": "github_list_workflow_runs",
|
| 748 |
+
"description": "List GitHub Actions CI/CD workflow runs and build status",
|
| 749 |
+
"parameters": {
|
| 750 |
+
"type": "object",
|
| 751 |
+
"properties": {
|
| 752 |
+
"owner": {"type": "string", "description": "Repository owner"},
|
| 753 |
+
"repo": {"type": "string", "description": "Repository name"}
|
| 754 |
+
},
|
| 755 |
+
"required": ["owner", "repo"]
|
| 756 |
+
}
|
| 757 |
+
}
|
| 758 |
}
|
| 759 |
]
|
| 760 |
|
|
|
|
| 887 |
except Exception as e:
|
| 888 |
return f"Error listing GitHub repos: {str(e)}"
|
| 889 |
|
| 890 |
+
async def github_get_repo_content(user_id: str, owner: str, repo: str, path: str = "", branch: str = None):
|
| 891 |
headers = await github_get_headers(user_id)
|
| 892 |
if not headers:
|
| 893 |
return "GitHub is not connected."
|
| 894 |
try:
|
| 895 |
url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}"
|
| 896 |
+
if branch:
|
| 897 |
+
url += f"?ref={branch}"
|
| 898 |
async with httpx.AsyncClient() as c:
|
| 899 |
res = await c.get(url, headers=headers)
|
| 900 |
if res.status_code != 200:
|
|
|
|
| 909 |
if data.get("encoding") == "base64" and data.get("content"):
|
| 910 |
content = base64.b64decode(data.get("content")).decode("utf-8", errors="ignore")
|
| 911 |
return content
|
| 912 |
+
return f"File Name: {data.get('name')} | Size: {data.get('size')} bytes | SHA: {data.get('sha')}"
|
| 913 |
return "Unable to parse content."
|
| 914 |
except Exception as e:
|
| 915 |
return f"Error reading GitHub content: {str(e)}"
|
| 916 |
|
| 917 |
+
async def github_create_or_update_file(user_id: str, owner: str, repo: str, path: str, content: str, message: str, branch: str = None):
|
| 918 |
+
headers = await github_get_headers(user_id)
|
| 919 |
+
if not headers:
|
| 920 |
+
return "GitHub is not connected."
|
| 921 |
+
try:
|
| 922 |
+
url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}"
|
| 923 |
+
encoded_content = base64.b64encode(content.encode("utf-8")).decode("utf-8")
|
| 924 |
+
payload = {
|
| 925 |
+
"message": message,
|
| 926 |
+
"content": encoded_content
|
| 927 |
+
}
|
| 928 |
+
if branch:
|
| 929 |
+
payload["branch"] = branch
|
| 930 |
+
async with httpx.AsyncClient() as c:
|
| 931 |
+
get_res = await c.get(url + (f"?ref={branch}" if branch else ""), headers=headers)
|
| 932 |
+
if get_res.status_code == 200:
|
| 933 |
+
file_info = get_res.json()
|
| 934 |
+
if isinstance(file_info, dict) and "sha" in file_info:
|
| 935 |
+
payload["sha"] = file_info["sha"]
|
| 936 |
+
res = await c.put(url, headers=headers, json=payload)
|
| 937 |
+
if res.status_code not in [200, 201]:
|
| 938 |
+
return f"Error updating file: {res.text}"
|
| 939 |
+
data = res.json()
|
| 940 |
+
return f"Successfully saved file '{path}' in commit: {data.get('commit', {}).get('sha')}"
|
| 941 |
+
except Exception as e:
|
| 942 |
+
return f"Error modifying file: {str(e)}"
|
| 943 |
+
|
| 944 |
+
async def github_delete_file(user_id: str, owner: str, repo: str, path: str, message: str, branch: str = None):
|
| 945 |
+
headers = await github_get_headers(user_id)
|
| 946 |
+
if not headers:
|
| 947 |
+
return "GitHub is not connected."
|
| 948 |
+
try:
|
| 949 |
+
url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}"
|
| 950 |
+
async with httpx.AsyncClient() as c:
|
| 951 |
+
get_res = await c.get(url + (f"?ref={branch}" if branch else ""), headers=headers)
|
| 952 |
+
if get_res.status_code != 200:
|
| 953 |
+
return f"File '{path}' not found."
|
| 954 |
+
file_info = get_res.json()
|
| 955 |
+
sha = file_info.get("sha")
|
| 956 |
+
payload = {"message": message, "sha": sha}
|
| 957 |
+
if branch: payload["branch"] = branch
|
| 958 |
+
del_res = await c.request("DELETE", url, headers=headers, json=payload)
|
| 959 |
+
if del_res.status_code != 200:
|
| 960 |
+
return f"Error deleting file: {del_res.text}"
|
| 961 |
+
return f"Successfully deleted file '{path}'."
|
| 962 |
+
except Exception as e:
|
| 963 |
+
return f"Error deleting file: {str(e)}"
|
| 964 |
+
|
| 965 |
+
async def github_create_branch(user_id: str, owner: str, repo: str, new_branch: str, base_branch: str = "main"):
|
| 966 |
+
headers = await github_get_headers(user_id)
|
| 967 |
+
if not headers:
|
| 968 |
+
return "GitHub is not connected."
|
| 969 |
+
try:
|
| 970 |
+
async with httpx.AsyncClient() as c:
|
| 971 |
+
ref_res = await c.get(f"https://api.github.com/repos/{owner}/{repo}/git/ref/heads/{base_branch}", headers=headers)
|
| 972 |
+
if ref_res.status_code != 200:
|
| 973 |
+
ref_res = await c.get(f"https://api.github.com/repos/{owner}/{repo}/git/ref/heads/master", headers=headers)
|
| 974 |
+
if ref_res.status_code != 200:
|
| 975 |
+
return f"Error getting base branch SHA: {ref_res.text}"
|
| 976 |
+
sha = ref_res.json().get("object", {}).get("sha")
|
| 977 |
+
payload = {
|
| 978 |
+
"ref": f"refs/heads/{new_branch}",
|
| 979 |
+
"sha": sha
|
| 980 |
+
}
|
| 981 |
+
res = await c.post(f"https://api.github.com/repos/{owner}/{repo}/git/refs", headers=headers, json=payload)
|
| 982 |
+
if res.status_code != 201:
|
| 983 |
+
return f"Error creating branch: {res.text}"
|
| 984 |
+
return f"Successfully created branch '{new_branch}'."
|
| 985 |
+
except Exception as e:
|
| 986 |
+
return f"Error creating branch: {str(e)}"
|
| 987 |
+
|
| 988 |
+
async def github_delete_branch(user_id: str, owner: str, repo: str, branch: str):
|
| 989 |
+
headers = await github_get_headers(user_id)
|
| 990 |
+
if not headers:
|
| 991 |
+
return "GitHub is not connected."
|
| 992 |
+
try:
|
| 993 |
+
url = f"https://api.github.com/repos/{owner}/{repo}/git/refs/heads/{branch}"
|
| 994 |
+
async with httpx.AsyncClient() as c:
|
| 995 |
+
res = await c.delete(url, headers=headers)
|
| 996 |
+
if res.status_code != 204:
|
| 997 |
+
return f"Error deleting branch: {res.text}"
|
| 998 |
+
return f"Successfully deleted branch '{branch}'."
|
| 999 |
+
except Exception as e:
|
| 1000 |
+
return f"Error deleting branch: {str(e)}"
|
| 1001 |
+
|
| 1002 |
+
async def github_list_commits(user_id: str, owner: str, repo: str, path: str = None, max_results: int = 10):
|
| 1003 |
+
headers = await github_get_headers(user_id)
|
| 1004 |
+
if not headers:
|
| 1005 |
+
return "GitHub is not connected."
|
| 1006 |
+
try:
|
| 1007 |
+
url = f"https://api.github.com/repos/{owner}/{repo}/commits?per_page={max_results}"
|
| 1008 |
+
if path: url += f"&path={path}"
|
| 1009 |
+
async with httpx.AsyncClient() as c:
|
| 1010 |
+
res = await c.get(url, headers=headers)
|
| 1011 |
+
if res.status_code != 200:
|
| 1012 |
+
return f"Error fetching commits: {res.text}"
|
| 1013 |
+
commits = res.json()
|
| 1014 |
+
items = []
|
| 1015 |
+
for cm in commits:
|
| 1016 |
+
sha = cm.get("sha", "")[:7]
|
| 1017 |
+
msg = cm.get("commit", {}).get("message", "").split("\n")[0]
|
| 1018 |
+
author = cm.get("commit", {}).get("author", {}).get("name", "")
|
| 1019 |
+
date = cm.get("commit", {}).get("author", {}).get("date", "")
|
| 1020 |
+
items.append(f"Commit [{sha}]: {msg} | Author: {author} | Date: {date}")
|
| 1021 |
+
return "\n".join(items)
|
| 1022 |
+
except Exception as e:
|
| 1023 |
+
return f"Error listing commits: {str(e)}"
|
| 1024 |
+
|
| 1025 |
+
async def github_list_pull_requests(user_id: str, owner: str, repo: str, state: str = "open"):
|
| 1026 |
+
headers = await github_get_headers(user_id)
|
| 1027 |
+
if not headers:
|
| 1028 |
+
return "GitHub is not connected."
|
| 1029 |
+
try:
|
| 1030 |
+
url = f"https://api.github.com/repos/{owner}/{repo}/pulls?state={state}"
|
| 1031 |
+
async with httpx.AsyncClient() as c:
|
| 1032 |
+
res = await c.get(url, headers=headers)
|
| 1033 |
+
if res.status_code != 200:
|
| 1034 |
+
return f"Error fetching PRs: {res.text}"
|
| 1035 |
+
prs = res.json()
|
| 1036 |
+
if not prs: return "No Pull Requests found."
|
| 1037 |
+
items = []
|
| 1038 |
+
for p in prs:
|
| 1039 |
+
items.append(f"PR #{p.get('number')}: {p.get('title')} | State: {p.get('state')} | User: {p.get('user', {}).get('login')} | Head: {p.get('head', {}).get('ref')} -> Base: {p.get('base', {}).get('ref')}")
|
| 1040 |
+
return "\n".join(items)
|
| 1041 |
+
except Exception as e:
|
| 1042 |
+
return f"Error listing PRs: {str(e)}"
|
| 1043 |
+
|
| 1044 |
+
async def github_get_pull_request(user_id: str, owner: str, repo: str, pull_number: int):
|
| 1045 |
+
headers = await github_get_headers(user_id)
|
| 1046 |
+
if not headers:
|
| 1047 |
+
return "GitHub is not connected."
|
| 1048 |
+
try:
|
| 1049 |
+
url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}"
|
| 1050 |
+
async with httpx.AsyncClient() as c:
|
| 1051 |
+
res = await c.get(url, headers=headers)
|
| 1052 |
+
if res.status_code != 200:
|
| 1053 |
+
return f"Error fetching PR detail: {res.text}"
|
| 1054 |
+
p = res.json()
|
| 1055 |
+
return f"PR #{p.get('number')}: {p.get('title')}\nState: {p.get('state')}\nMergeable: {p.get('mergeable')}\nBody:\n{p.get('body')}"
|
| 1056 |
+
except Exception as e:
|
| 1057 |
+
return f"Error fetching PR detail: {str(e)}"
|
| 1058 |
+
|
| 1059 |
+
async def github_create_pull_request(user_id: str, owner: str, repo: str, title: str, head: str, base: str = "main", body: str = ""):
|
| 1060 |
+
headers = await github_get_headers(user_id)
|
| 1061 |
+
if not headers:
|
| 1062 |
+
return "GitHub is not connected."
|
| 1063 |
+
try:
|
| 1064 |
+
url = f"https://api.github.com/repos/{owner}/{repo}/pulls"
|
| 1065 |
+
payload = {"title": title, "head": head, "base": base, "body": body}
|
| 1066 |
+
async with httpx.AsyncClient() as c:
|
| 1067 |
+
res = await c.post(url, headers=headers, json=payload)
|
| 1068 |
+
if res.status_code not in [200, 201]:
|
| 1069 |
+
return f"Error creating PR: {res.text}"
|
| 1070 |
+
p = res.json()
|
| 1071 |
+
return f"Successfully created PR #{p.get('number')}: {p.get('html_url')}"
|
| 1072 |
+
except Exception as e:
|
| 1073 |
+
return f"Error creating PR: {str(e)}"
|
| 1074 |
+
|
| 1075 |
+
async def github_merge_pull_request(user_id: str, owner: str, repo: str, pull_number: int, commit_title: str = None):
|
| 1076 |
+
headers = await github_get_headers(user_id)
|
| 1077 |
+
if not headers:
|
| 1078 |
+
return "GitHub is not connected."
|
| 1079 |
+
try:
|
| 1080 |
+
url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}/merge"
|
| 1081 |
+
payload = {}
|
| 1082 |
+
if commit_title: payload["commit_title"] = commit_title
|
| 1083 |
+
async with httpx.AsyncClient() as c:
|
| 1084 |
+
res = await c.put(url, headers=headers, json=payload)
|
| 1085 |
+
if res.status_code != 200:
|
| 1086 |
+
return f"Error merging PR: {res.text}"
|
| 1087 |
+
return f"Successfully merged PR #{pull_number}."
|
| 1088 |
+
except Exception as e:
|
| 1089 |
+
return f"Error merging PR: {str(e)}"
|
| 1090 |
+
|
| 1091 |
async def github_list_issues(user_id: str, owner: str, repo: str, state: str = "open"):
|
| 1092 |
headers = await github_get_headers(user_id)
|
| 1093 |
if not headers:
|
|
|
|
| 1124 |
except Exception as e:
|
| 1125 |
return f"Error creating GitHub issue: {str(e)}"
|
| 1126 |
|
| 1127 |
+
async def github_update_issue(user_id: str, owner: str, repo: str, issue_number: int, title: str = None, body: str = None, state: str = None):
|
| 1128 |
+
headers = await github_get_headers(user_id)
|
| 1129 |
+
if not headers:
|
| 1130 |
+
return "GitHub is not connected."
|
| 1131 |
+
try:
|
| 1132 |
+
url = f"https://api.github.com/repos/{owner}/{repo}/issues/{issue_number}"
|
| 1133 |
+
payload = {}
|
| 1134 |
+
if title: payload["title"] = title
|
| 1135 |
+
if body: payload["body"] = body
|
| 1136 |
+
if state: payload["state"] = state
|
| 1137 |
+
async with httpx.AsyncClient() as c:
|
| 1138 |
+
res = await c.patch(url, headers=headers, json=payload)
|
| 1139 |
+
if res.status_code != 200:
|
| 1140 |
+
return f"Error updating issue: {res.text}"
|
| 1141 |
+
return f"Successfully updated Issue #{issue_number}."
|
| 1142 |
+
except Exception as e:
|
| 1143 |
+
return f"Error updating issue: {str(e)}"
|
| 1144 |
+
|
| 1145 |
+
async def github_create_comment(user_id: str, owner: str, repo: str, issue_number: int, body: str):
|
| 1146 |
+
headers = await github_get_headers(user_id)
|
| 1147 |
+
if not headers:
|
| 1148 |
+
return "GitHub is not connected."
|
| 1149 |
+
try:
|
| 1150 |
+
url = f"https://api.github.com/repos/{owner}/{repo}/issues/{issue_number}/comments"
|
| 1151 |
+
payload = {"body": body}
|
| 1152 |
+
async with httpx.AsyncClient() as c:
|
| 1153 |
+
res = await c.post(url, headers=headers, json=payload)
|
| 1154 |
+
if res.status_code not in [200, 201]:
|
| 1155 |
+
return f"Error adding comment: {res.text}"
|
| 1156 |
+
return f"Successfully added comment to Issue/PR #{issue_number}."
|
| 1157 |
+
except Exception as e:
|
| 1158 |
+
return f"Error adding comment: {str(e)}"
|
| 1159 |
+
|
| 1160 |
+
async def github_search_code(user_id: str, query: str):
|
| 1161 |
+
headers = await github_get_headers(user_id)
|
| 1162 |
+
if not headers:
|
| 1163 |
+
return "GitHub is not connected."
|
| 1164 |
+
try:
|
| 1165 |
+
encoded_q = urllib.parse.quote(query)
|
| 1166 |
+
url = f"https://api.github.com/search/code?q={encoded_q}"
|
| 1167 |
+
async with httpx.AsyncClient() as c:
|
| 1168 |
+
res = await c.get(url, headers=headers)
|
| 1169 |
+
if res.status_code != 200:
|
| 1170 |
+
return f"Error searching code: {res.text}"
|
| 1171 |
+
data = res.json()
|
| 1172 |
+
items = data.get("items", [])
|
| 1173 |
+
if not items: return "No code matches found."
|
| 1174 |
+
results = []
|
| 1175 |
+
for item in items[:10]:
|
| 1176 |
+
results.append(f"Repo: {item.get('repository', {}).get('full_name')} | File: {item.get('path')} | URL: {item.get('html_url')}")
|
| 1177 |
+
return "\n".join(results)
|
| 1178 |
+
except Exception as e:
|
| 1179 |
+
return f"Error searching code: {str(e)}"
|
| 1180 |
+
|
| 1181 |
+
async def github_list_workflow_runs(user_id: str, owner: str, repo: str):
|
| 1182 |
+
headers = await github_get_headers(user_id)
|
| 1183 |
+
if not headers:
|
| 1184 |
+
return "GitHub is not connected."
|
| 1185 |
+
try:
|
| 1186 |
+
url = f"https://api.github.com/repos/{owner}/{repo}/actions/runs?per_page=5"
|
| 1187 |
+
async with httpx.AsyncClient() as c:
|
| 1188 |
+
res = await c.get(url, headers=headers)
|
| 1189 |
+
if res.status_code != 200:
|
| 1190 |
+
return f"Error fetching workflow runs: {res.text}"
|
| 1191 |
+
runs = res.json().get("workflow_runs", [])
|
| 1192 |
+
if not runs: return "No CI/CD workflow runs found."
|
| 1193 |
+
items = []
|
| 1194 |
+
for r in runs:
|
| 1195 |
+
items.append(f"Run #{r.get('run_number')}: {r.get('name')} | Status: {r.get('status')} | Conclusion: {r.get('conclusion')} | Branch: {r.get('head_branch')}")
|
| 1196 |
+
return "\n".join(items)
|
| 1197 |
+
except Exception as e:
|
| 1198 |
+
return f"Error fetching workflow runs: {str(e)}"
|
| 1199 |
+
|
| 1200 |
async def notion_get_headers(user_id: str):
|
| 1201 |
token_data = await get_token_from_supabase(user_id, "notion")
|
| 1202 |
if not token_data:
|
|
|
|
| 1702 |
elif func_name == "github_list_repos":
|
| 1703 |
return await github_list_repos(user_id, args.get("max_results", 10))
|
| 1704 |
elif func_name == "github_get_repo_content":
|
| 1705 |
+
return await github_get_repo_content(user_id, args.get("owner", ""), args.get("repo", ""), args.get("path", ""), args.get("branch"))
|
| 1706 |
+
elif func_name == "github_create_or_update_file":
|
| 1707 |
+
return await github_create_or_update_file(user_id, args.get("owner", ""), args.get("repo", ""), args.get("path", ""), args.get("content", ""), args.get("message", ""), args.get("branch"))
|
| 1708 |
+
elif func_name == "github_delete_file":
|
| 1709 |
+
return await github_delete_file(user_id, args.get("owner", ""), args.get("repo", ""), args.get("path", ""), args.get("message", ""), args.get("branch"))
|
| 1710 |
+
elif func_name == "github_create_branch":
|
| 1711 |
+
return await github_create_branch(user_id, args.get("owner", ""), args.get("repo", ""), args.get("new_branch", ""), args.get("base_branch", "main"))
|
| 1712 |
+
elif func_name == "github_delete_branch":
|
| 1713 |
+
return await github_delete_branch(user_id, args.get("owner", ""), args.get("repo", ""), args.get("branch", ""))
|
| 1714 |
+
elif func_name == "github_list_commits":
|
| 1715 |
+
return await github_list_commits(user_id, args.get("owner", ""), args.get("repo", ""), args.get("path"), args.get("max_results", 10))
|
| 1716 |
+
elif func_name == "github_list_pull_requests":
|
| 1717 |
+
return await github_list_pull_requests(user_id, args.get("owner", ""), args.get("repo", ""), args.get("state", "open"))
|
| 1718 |
+
elif func_name == "github_get_pull_request":
|
| 1719 |
+
return await github_get_pull_request(user_id, args.get("owner", ""), args.get("repo", ""), args.get("pull_number", 1))
|
| 1720 |
+
elif func_name == "github_create_pull_request":
|
| 1721 |
+
return await github_create_pull_request(user_id, args.get("owner", ""), args.get("repo", ""), args.get("title", ""), args.get("head", ""), args.get("base", "main"), args.get("body", ""))
|
| 1722 |
+
elif func_name == "github_merge_pull_request":
|
| 1723 |
+
return await github_merge_pull_request(user_id, args.get("owner", ""), args.get("repo", ""), args.get("pull_number", 1), args.get("commit_title"))
|
| 1724 |
elif func_name == "github_list_issues":
|
| 1725 |
return await github_list_issues(user_id, args.get("owner", ""), args.get("repo", ""), args.get("state", "open"))
|
| 1726 |
elif func_name == "github_create_issue":
|
| 1727 |
return await github_create_issue(user_id, args.get("owner", ""), args.get("repo", ""), args.get("title", ""), args.get("body", ""))
|
| 1728 |
+
elif func_name == "github_update_issue":
|
| 1729 |
+
return await github_update_issue(user_id, args.get("owner", ""), args.get("repo", ""), args.get("issue_number", 1), args.get("title"), args.get("body"), args.get("state"))
|
| 1730 |
+
elif func_name == "github_create_comment":
|
| 1731 |
+
return await github_create_comment(user_id, args.get("owner", ""), args.get("repo", ""), args.get("issue_number", 1), args.get("body", ""))
|
| 1732 |
+
elif func_name == "github_search_code":
|
| 1733 |
+
return await github_search_code(user_id, args.get("query", ""))
|
| 1734 |
+
elif func_name == "github_list_workflow_runs":
|
| 1735 |
+
return await github_list_workflow_runs(user_id, args.get("owner", ""), args.get("repo", ""))
|
| 1736 |
return "Unknown tool function."
|
| 1737 |
|
| 1738 |
def count_tokens(messages):
|