mirror of
https://github.com/coalaura/whiskr.git
synced 2025-09-08 17:06:42 +00:00
github tool
This commit is contained in:
@@ -17,9 +17,10 @@ whiskr is a private, self-hosted web chat interface for interacting with AI mode
|
|||||||
- Tags indicate if a model supports **tools**, **vision**, or **reasoning**
|
- Tags indicate if a model supports **tools**, **vision**, or **reasoning**
|
||||||
- Search field with fuzzy matching to quickly find models
|
- Search field with fuzzy matching to quickly find models
|
||||||
- Models are listed newest -> oldest
|
- Models are listed newest -> oldest
|
||||||
- Web search tools (set the `EXA_TOKEN` to enable):
|
- Web search tools (set the `tokens.exa` to enable):
|
||||||
- `search_web`: search via Exa in auto mode; returns up to 10 results with short summaries
|
- `search_web`: search via Exa in auto mode; returns up to 10 results with short summaries
|
||||||
- `fetch_contents`: fetch page contents for one or more URLs via Exa /contents
|
- `fetch_contents`: fetch page contents for one or more URLs via Exa /contents
|
||||||
|
- `github_repository`: get a quick overview of a GitHub repository (repo info, up to 20 branches, top-level files/dirs, and the README) without cloning (optionally set `tokens.github`)
|
||||||
- Images attachments for vision models using simple markdown image tags
|
- Images attachments for vision models using simple markdown image tags
|
||||||
- Text/Code file attachments
|
- Text/Code file attachments
|
||||||
- Reasoning effort control
|
- Reasoning effort control
|
||||||
@@ -30,7 +31,6 @@ whiskr is a private, self-hosted web chat interface for interacting with AI mode
|
|||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- tool for github repo overviews (included in search tools)
|
|
||||||
- settings
|
- settings
|
||||||
- auto-retry on edit
|
- auto-retry on edit
|
||||||
- ctrl+enter vs enter for sending
|
- ctrl+enter vs enter for sending
|
||||||
|
9
chat.go
9
chat.go
@@ -137,7 +137,7 @@ func (r *Request) Parse() (*openrouter.ChatCompletionRequest, error) {
|
|||||||
request.Tools = GetSearchTools()
|
request.Tools = GetSearchTools()
|
||||||
request.ToolChoice = "auto"
|
request.ToolChoice = "auto"
|
||||||
|
|
||||||
request.Messages = append(request.Messages, openrouter.SystemMessage("You have access to web search tools. Use `search_web` with `query` (string) and `num_results` (1-10) to find current information and get result summaries. Use `fetch_contents` with `urls` (array) to read full page content. Always specify all parameters for each tool call. Call only one tool per response."))
|
request.Messages = append(request.Messages, openrouter.SystemMessage(InternalToolsPrompt))
|
||||||
}
|
}
|
||||||
|
|
||||||
for index, message := range r.Messages {
|
for index, message := range r.Messages {
|
||||||
@@ -302,6 +302,13 @@ func HandleChat(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
response.Send(ErrorChunk(err))
|
response.Send(ErrorChunk(err))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case "github_repository":
|
||||||
|
err = HandleGitHubRepositoryTool(ctx, tool)
|
||||||
|
if err != nil {
|
||||||
|
response.Send(ErrorChunk(err))
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
2
env.go
2
env.go
@@ -15,6 +15,7 @@ type EnvTokens struct {
|
|||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
OpenRouter string `json:"openrouter"`
|
OpenRouter string `json:"openrouter"`
|
||||||
Exa string `json:"exa"`
|
Exa string `json:"exa"`
|
||||||
|
GitHub string `json:"github"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type EnvSettings struct {
|
type EnvSettings struct {
|
||||||
@@ -119,6 +120,7 @@ func (e *Environment) Store() error {
|
|||||||
"$.tokens.secret": {yaml.HeadComment(" server secret for signing auth tokens; auto-generated if empty")},
|
"$.tokens.secret": {yaml.HeadComment(" server secret for signing auth tokens; auto-generated if empty")},
|
||||||
"$.tokens.openrouter": {yaml.HeadComment(" openrouter.ai api token (required)")},
|
"$.tokens.openrouter": {yaml.HeadComment(" openrouter.ai api token (required)")},
|
||||||
"$.tokens.exa": {yaml.HeadComment(" exa search api token (optional; used by search tools)")},
|
"$.tokens.exa": {yaml.HeadComment(" exa search api token (optional; used by search tools)")},
|
||||||
|
"$.tokens.github": {yaml.HeadComment(" github api token (optional; used by search tools)")},
|
||||||
|
|
||||||
"$.settings.cleanup": {yaml.HeadComment(" normalize unicode in assistant output (optional; default: true)")},
|
"$.settings.cleanup": {yaml.HeadComment(" normalize unicode in assistant output (optional; default: true)")},
|
||||||
|
|
||||||
|
@@ -8,6 +8,8 @@ tokens:
|
|||||||
openrouter: ""
|
openrouter: ""
|
||||||
# exa search api token (optional; used by search tools)
|
# exa search api token (optional; used by search tools)
|
||||||
exa: ""
|
exa: ""
|
||||||
|
# github api token (optional; used by search tools)
|
||||||
|
github: ""
|
||||||
|
|
||||||
settings:
|
settings:
|
||||||
# normalize unicode in assistant output (optional; default: true)
|
# normalize unicode in assistant output (optional; default: true)
|
||||||
|
240
github.go
Normal file
240
github.go
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GitHubRepo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
HtmlURL string `json:"html_url"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Stargazers int `json:"stargazers_count"`
|
||||||
|
Forks int `json:"forks_count"`
|
||||||
|
Visibility string `json:"visibility"`
|
||||||
|
DefaultBranch string `json:"default_branch"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GitHubContent struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GitHubReadme struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Encoding string `json:"encoding"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GitHubReadme) AsText() (string, error) {
|
||||||
|
if r.Encoding == "base64" {
|
||||||
|
content, err := base64.StdEncoding.DecodeString(r.Content)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(content), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGitHubRequest(ctx context.Context, path string) (*http.Request, error) {
|
||||||
|
req, err := http.NewRequest("GET", fmt.Sprintf("https://api.github.com%s", path), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
|
req.Header.Set("Accept", "application/vnd.github+json")
|
||||||
|
|
||||||
|
if env.Tokens.GitHub != "" {
|
||||||
|
req.Header.Set("Authorization", "Bearer "+env.Tokens.GitHub)
|
||||||
|
}
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GitHubRepositoryJson(ctx context.Context, owner, repo string) (*GitHubRepo, error) {
|
||||||
|
req, err := NewGitHubRequest(ctx, fmt.Sprintf("/repos/%s/%s", owner, repo))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var response GitHubRepo
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Name == "" {
|
||||||
|
return nil, errors.New("error getting data")
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Description == "" {
|
||||||
|
response.Description = "(none)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GitHubRepositoryReadmeJson(ctx context.Context, owner, repo, branch string) (*GitHubReadme, error) {
|
||||||
|
req, err := NewGitHubRequest(ctx, fmt.Sprintf("/repos/%s/%s/readme?ref=%s", owner, repo, branch))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var response GitHubReadme
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GitHubRepositoryContentsJson(ctx context.Context, owner, repo, branch string) ([]GitHubContent, error) {
|
||||||
|
req, err := NewGitHubRequest(ctx, fmt.Sprintf("/repos/%s/%s/contents?ref=%s", owner, repo, branch))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var response []GitHubContent
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RepoOverview(ctx context.Context, arguments GitHubRepositoryArguments) (string, error) {
|
||||||
|
repository, err := GitHubRepositoryJson(ctx, arguments.Owner, arguments.Repo)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
wg sync.WaitGroup
|
||||||
|
|
||||||
|
readmeMarkdown string
|
||||||
|
directories []string
|
||||||
|
files []string
|
||||||
|
)
|
||||||
|
|
||||||
|
// fetch readme
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
readme, err := GitHubRepositoryReadmeJson(ctx, arguments.Owner, arguments.Repo, repository.DefaultBranch)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to get repository readme: %v\n", err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
markdown, err := readme.AsText()
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to decode repository readme: %v\n", err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
readmeMarkdown = markdown
|
||||||
|
}()
|
||||||
|
|
||||||
|
// fetch contents
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
contents, err := GitHubRepositoryContentsJson(ctx, arguments.Owner, arguments.Repo, repository.DefaultBranch)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to get repository contents: %v\n", err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, content := range contents {
|
||||||
|
switch content.Type {
|
||||||
|
case "dir":
|
||||||
|
directories = append(directories, content.Name)
|
||||||
|
case "file":
|
||||||
|
files = append(files, content.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(directories)
|
||||||
|
sort.Strings(files)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// wait and combine results
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
fmt.Fprintf(&builder, "### %s (%s)\n", repository.Name, repository.Visibility)
|
||||||
|
fmt.Fprintf(&builder, "- URL: %s\n", repository.HtmlURL)
|
||||||
|
fmt.Fprintf(&builder, "- Description: %s\n", strings.ReplaceAll(repository.Description, "\n", " "))
|
||||||
|
fmt.Fprintf(&builder, "- Default branch: %s\n", repository.DefaultBranch)
|
||||||
|
fmt.Fprintf(&builder, "- Stars: %d | Forks: %d\n", repository.Stargazers, repository.Forks)
|
||||||
|
|
||||||
|
builder.WriteString("\n### Top-level files and directories\n")
|
||||||
|
|
||||||
|
if len(directories) == 0 && len(files) == 0 {
|
||||||
|
builder.WriteString("*No entries or insufficient permissions.*\n")
|
||||||
|
} else {
|
||||||
|
for _, directory := range directories {
|
||||||
|
fmt.Fprintf(&builder, "- [D] %s\n", directory)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
fmt.Fprintf(&builder, "- [F] %s\n", file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("\n### README\n")
|
||||||
|
|
||||||
|
if readmeMarkdown == "" {
|
||||||
|
builder.WriteString("*No README found or could not load.*\n")
|
||||||
|
} else {
|
||||||
|
builder.WriteString(readmeMarkdown)
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.String(), nil
|
||||||
|
}
|
4
go.sum
4
go.sum
@@ -22,8 +22,6 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/revrost/go-openrouter v0.2.1 h1:4BMQ6pgYeEJq9pLl7pFbwnBabmqgUa35hGRnVHqjpA4=
|
|
||||||
github.com/revrost/go-openrouter v0.2.1/go.mod h1:ZH/UdpnDEdMmJwq8tbSTX1S5I07ee8KMlEYN4jmegU0=
|
|
||||||
github.com/revrost/go-openrouter v0.2.2 h1:7bOdLPKmw0iJB1AdpN+YaWUd2XC9cwfJKDY10iaSAzI=
|
github.com/revrost/go-openrouter v0.2.2 h1:7bOdLPKmw0iJB1AdpN+YaWUd2XC9cwfJKDY10iaSAzI=
|
||||||
github.com/revrost/go-openrouter v0.2.2/go.mod h1:ZH/UdpnDEdMmJwq8tbSTX1S5I07ee8KMlEYN4jmegU0=
|
github.com/revrost/go-openrouter v0.2.2/go.mod h1:ZH/UdpnDEdMmJwq8tbSTX1S5I07ee8KMlEYN4jmegU0=
|
||||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
@@ -33,8 +31,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
|
||||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
|
||||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
||||||
|
1
internal/tools.txt
Normal file
1
internal/tools.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
You have access to web search tools. Use `search_web` with `query` (string) and `num_results` (1-10) to find current information and get result summaries. Use `fetch_contents` with `urls` (array) to read full page content. Use `github_repository` with `owner` (string) and `repo` (string) to get a quick overview of a GitHub repository (repo info, up to 20 branches, top-level files/dirs, and the README) without cloning. Always specify all parameters for each tool call. Call only one tool per response.
|
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
_ "embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
@@ -27,6 +28,9 @@ type Prompt struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
//go:embed internal/tools.txt
|
||||||
|
InternalToolsPrompt string
|
||||||
|
|
||||||
Prompts []Prompt
|
Prompts []Prompt
|
||||||
Templates = make(map[string]*template.Template)
|
Templates = make(map[string]*template.Template)
|
||||||
)
|
)
|
||||||
|
48
search.go
48
search.go
@@ -19,6 +19,11 @@ type FetchContentsArguments struct {
|
|||||||
URLs []string `json:"urls"`
|
URLs []string `json:"urls"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GitHubRepositoryArguments struct {
|
||||||
|
Owner string `json:"owner"`
|
||||||
|
Repo string `json:"repo"`
|
||||||
|
}
|
||||||
|
|
||||||
func GetSearchTools() []openrouter.Tool {
|
func GetSearchTools() []openrouter.Tool {
|
||||||
return []openrouter.Tool{
|
return []openrouter.Tool{
|
||||||
{
|
{
|
||||||
@@ -66,6 +71,29 @@ func GetSearchTools() []openrouter.Tool {
|
|||||||
Strict: true,
|
Strict: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Type: openrouter.ToolTypeFunction,
|
||||||
|
Function: &openrouter.FunctionDefinition{
|
||||||
|
Name: "github_repository",
|
||||||
|
Description: "Get a quick overview of a GitHub repository without cloning: repo info, up to 20 branches (popular first), top-level files/dirs, and the README.",
|
||||||
|
Parameters: map[string]any{
|
||||||
|
"type": "object",
|
||||||
|
"required": []string{"owner", "repo"},
|
||||||
|
"properties": map[string]any{
|
||||||
|
"owner": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
"description": "GitHub username or organization (e.g., 'torvalds').",
|
||||||
|
},
|
||||||
|
"repo": map[string]any{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Repository name (e.g., 'linux').",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
},
|
||||||
|
Strict: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,3 +156,23 @@ func HandleFetchContentsTool(ctx context.Context, tool *ToolCall) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HandleGitHubRepositoryTool(ctx context.Context, tool *ToolCall) error {
|
||||||
|
var arguments GitHubRepositoryArguments
|
||||||
|
|
||||||
|
err := json.Unmarshal([]byte(tool.Args), &arguments)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := RepoOverview(ctx, arguments)
|
||||||
|
if err != nil {
|
||||||
|
tool.Result = fmt.Sprintf("error: %v", err)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tool.Result = result
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user