Files
LocalAI/addons/github/internal/platform/platform.go
T
Ettore Di Giacinto 3d687a209c feat(addons): GitHub add-on skeleton and read tools
Add the github add-on: manifest with a credential slot, a read-only
tool set (code search, files, trees, issues, pull requests and diffs)
with size caps and untrusted-content framing, rate-limit mapping, a
connector route that checks the token, and a fake GitHub API for tests.

Assisted-by: Claude:claude-sonnet-5-5 [go]
2026-11-03 14:20:12 +00:00

79 lines
2.5 KiB
Go

// SPDX-License-Identifier: MIT
// Package platform is the connector route of the GitHub add-on. In this
// version it carries no messages: a binding of kind github holds the token and
// the repository list that the read tools use, and the host's Test call asks
// "me" to check the token. Listening for mentions comes later, so poll waits
// and returns nothing, and the ops that send fail with a plain reason.
package platform
import (
"context"
"errors"
"strconv"
"time"
"github.com/mudler/LocalAI/addons/github/internal/ghapi"
"github.com/mudler/LocalAI/addons/internal/connector"
"github.com/mudler/LocalAI/pkg/addons/contract"
)
// Kind is the one kind of binding this add-on serves.
const Kind = "github"
// ErrNoMessages is what the ops that send answer until the connector exists.
var ErrNoMessages = errors.New("this add-on does not carry messages yet")
// Platform is GitHub as a connector.Platform.
type Platform struct{ API *ghapi.Client }
var _ connector.Platform = Platform{}
func (Platform) Kind() string { return Kind }
func (Platform) MaxRunes() int { return 65000 }
// Poll returns no update. It waits for the time the host allows so the loop
// of an enabled binding does not spin.
func (Platform) Poll(ctx context.Context, _ string, _ int64, wait int) ([]contract.ConnectorMessage, error) {
if wait <= 0 {
return nil, nil
}
t := time.NewTimer(time.Duration(wait) * time.Second)
defer t.Stop()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-t.C:
return nil, nil
}
}
func (Platform) Send(context.Context, contract.ConnectorRequest) (string, error) {
return "", ErrNoMessages
}
func (Platform) Edit(context.Context, contract.ConnectorRequest) error { return ErrNoMessages }
func (Platform) Typing(context.Context, contract.ConnectorRequest) error { return nil }
// Me returns the login of the token.
func (p Platform) Me(ctx context.Context, token string) (connector.Bot, error) {
var me struct {
ID int64 `json:"id"`
Login string `json:"login"`
}
if _, err := p.API.GetJSON(ctx, token, "/user", nil, &me); err != nil {
return connector.Bot{}, mapErr(err)
}
return connector.Bot{ID: strconv.FormatInt(me.ID, 10), Username: me.Login}, nil
}
func mapErr(err error) error {
var limited *contract.ErrRateLimited
switch {
case errors.Is(err, ghapi.ErrUnauthorized):
return connector.ErrUnauthorized
case errors.As(err, &limited):
return &connector.RetryAfter{Seconds: limited.Seconds()}
}
return errors.New(ghapi.Describe(err))
}