Skip to content

Commit

Permalink
Code for walking the graph.
Browse files Browse the repository at this point in the history
  • Loading branch information
jlewi committed Nov 9, 2024
1 parent 0694924 commit 9c8e1cd
Show file tree
Hide file tree
Showing 16 changed files with 650 additions and 12 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ There are two ways to collaborate and improve the algorithm
cd ~/git_bskylists
git fetch origin
git checkout origin/main
cd ~/git_bsky
make build
cd ~/git_bskyctl
#make build
.build/bsctl apply ~/git_bskylists/aiengineering.yaml
.build/bsctl apply ~/git_bskylists/platformengineering.yaml
```
Expand Down
11 changes: 7 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ require (
github.com/bluesky-social/indigo v0.0.0-20240503174839-ef8e99bfcc76
github.com/fatih/color v1.16.0
github.com/go-logr/zapr v1.3.0
github.com/google/go-cmp v0.6.0
github.com/gorilla/websocket v1.5.1
github.com/ipfs/go-cid v0.4.1
github.com/jlewi/goapp-template v0.0.0-20241024231018-3cd83e9d4465
github.com/jlewi/monogo v0.0.0-20240918030136-e0ca1337aea4
github.com/maxence-charriere/go-app/v9 v9.8.0
github.com/pkg/errors v0.9.1
github.com/sashabaranov/go-openai v1.32.5
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/urfave/cli/v2 v2.27.2
Expand All @@ -25,8 +26,11 @@ require (
cloud.google.com/go v0.112.1 // indirect
cloud.google.com/go/compute v1.24.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.6 // indirect
cloud.google.com/go/logging v1.9.0 // indirect
cloud.google.com/go/longrunning v0.5.5 // indirect
cloud.google.com/go/secretmanager v1.11.5 // indirect
cloud.google.com/go/storage v1.38.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-cmd/cmd v1.4.1 // indirect
Expand All @@ -37,7 +41,6 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
Expand Down Expand Up @@ -82,13 +85,13 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/logr v1.4.1
github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
github.com/hashicorp/go-retryablehttp v0.7.5
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/golang-lru/arc/v2 v2.0.7 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
Expand Down
10 changes: 8 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ cloud.google.com/go/logging v1.9.0 h1:iEIOXFO9EmSiTjDmfpbRjOxECO7R8C7b8IXUGOj7xZ
cloud.google.com/go/logging v1.9.0/go.mod h1:1Io0vnZv4onoUnsVUQY3HZ3Igb1nBchky0A0y7BBBhE=
cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMILWnOg=
cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s=
cloud.google.com/go/secretmanager v1.11.5 h1:82fpF5vBBvu9XW4qj0FU2C6qVMtj1RM/XHwKXUEAfYY=
cloud.google.com/go/secretmanager v1.11.5/go.mod h1:eAGv+DaCHkeVyQi0BeXgAHOU0RdrMeZIASKc+S7VqH4=
cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg=
cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
Expand Down Expand Up @@ -122,6 +126,8 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
Expand Down Expand Up @@ -245,8 +251,6 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jlewi/goapp-template v0.0.0-20241024231018-3cd83e9d4465 h1:r3hh5SWQXvXmLEOEPhyY8lA4U5PkIZuAvn63VfdaDmI=
github.com/jlewi/goapp-template v0.0.0-20241024231018-3cd83e9d4465/go.mod h1:JoLak4ZzQLs/IAUf/ohJv1URV7L/H4i64fzcCb9S//Q=
github.com/jlewi/monogo v0.0.0-20240918030136-e0ca1337aea4 h1:xQWqHY7FItn2FifFeQ5S/9kPgWpbGpN7qjH1IBihgaM=
github.com/jlewi/monogo v0.0.0-20240918030136-e0ca1337aea4/go.mod h1:s3nTD+owHZ6b+F13JdSpXLtrAH35pOqdwdcZEZ/gwBc=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
Expand Down Expand Up @@ -387,6 +391,8 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sashabaranov/go-openai v1.32.5 h1:/eNVa8KzlE7mJdKPZDj6886MUzZQjoVHyn0sLvIt5qA=
github.com/sashabaranov/go-openai v1.32.5/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
Expand Down
18 changes: 17 additions & 1 deletion pkg/api/v1alpha1/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,26 @@ type AccountList struct {

// DID is the Decentralized Identifier for the list
// TOOD(jeremy):
DID string `json:"did" yaml:"did"`
DID string `json:"did" yaml:"did"`
// TODO(jeremy): Get rid of this; use Accounts
Accounts []Account `json:"accounts" yaml:"accounts"`

// TODO(jeremy): Is it better to have include and exclude lists or just a single list with a field to indicate
// whether to include or exclude?

Members []Membership `json:"members" yaml:"members"`
Exclude []Membership `json:"exclude" yaml:"exclude"`
}

type Membership struct {
Account Account `json:"account" yaml:"account"`
// Explanation is a string explaining why the account is in the list
Explanation string `json:"reason" yaml:"reason"`
}

type Account struct {
Handle string `json:"handle" yaml:"handle"`
// DID is the Decentralized Identifier for the account
// If the DID is specified it will be used and Handle will be ignored as Handles are mutable but DIDs are not.
DID string `json:"did" yaml:"did"`
}
45 changes: 45 additions & 0 deletions pkg/api/v1alpha1/community.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package v1alpha1

import "k8s.io/apimachinery/pkg/runtime/schema"

var (
CommunityBuilderKind = "CommunityBuilder"
CommunityBuilderGVK = schema.FromAPIVersionAndKind(Group+"/"+Version, CommunityBuilderKind)
)

// TODO(jeremy): I think the name should be a noun not a verb.

type CommunityBuilder struct {
APIVersion string `json:"apiVersion" yaml:"apiVersion"`
Kind string `json:"kind" yaml:"kind"`
Metadata Metadata `json:"metadata" yaml:"metadata"`

// Definition is the definition of the community. It parameterizes the LLM prompt to classifiy accounts
Definition CommunityDefinition `json:"definition" yaml:"definition"`
// Seeds is a list of accounts to seed the graph with
Seeds []Account `json:"seeds" yaml:"seeds"`

// OutputFile is the file to write the AccountList to
OutputFile string `json:"outputFile" yaml:"outputFile"`
}

type CommunityDefinition struct {
// Name is the name of the community
Name string `json:"name" yaml:"name"`

// Criterion is a list of criterion for including accounts in the community
Criterion []string `json:"criterion" yaml:"criterion"`

// Example is a list of examples to help classify accounts
Examples []ProfileExample `json:"examples" yaml:"examples"`
}

type ProfileExample struct {
// Profile is the example profile
Profile string `json:"profile" yaml:"profile"`
// Member is true or false depending on whether the profile is a member of the community
Member bool `json:"member" yaml:"member"`

// Explanation is the explanation of why the profile is a member or not
Explanation string `json:"explanation" yaml:"explanation"`
}
22 changes: 22 additions & 0 deletions pkg/application/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import (
"github.com/jlewi/bsctl/pkg/config"
"github.com/jlewi/bsctl/pkg/controllers"
"github.com/jlewi/bsctl/pkg/lists"
"github.com/jlewi/bsctl/pkg/oai"
"github.com/jlewi/bsctl/pkg/util"
"github.com/jlewi/monogo/gcp/logging"
"github.com/jlewi/monogo/helpers"
"github.com/pkg/errors"
"github.com/sashabaranov/go-openai"
"github.com/spf13/cobra"
"go.uber.org/zap"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand Down Expand Up @@ -116,6 +118,18 @@ func (a *App) SetupRegistry() error {
return err
}

oaiClient, err := a.GetOAIClient(context.Background())
if err != nil {
return errors.Wrapf(err, "Failed to create OAI client")
}
walker, err := lists.NewWalker(client, oaiClient)
if err != nil {
return err
}
if err := a.Registry.Register(v1alpha1.CommunityBuilderGVK, walker); err != nil {
return err
}

return nil
}

Expand Down Expand Up @@ -171,6 +185,14 @@ func (a *App) TidyPaths(ctx context.Context, inPaths []string) error {
return nil
}

func (a *App) GetOAIClient(ctx context.Context) (*openai.Client, error) {
if a.Config == nil {
return nil, errors.WithStack(errors.New("app.Config is nil; call app.LoadConfig"))
}

return oai.NewClient(*a.Config)
}

func (a *App) apply(ctx context.Context, path string) error {
if a.Registry == nil {
return errors.New("Registry is nil; call SetupRegistry first")
Expand Down
10 changes: 10 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ type Config struct {
Password string `json:"password" yaml:"password"`
Prefix string `json:"prefix" yaml:"prefix"`

OpenAI *OpenAIConfig `json:"openai,omitempty" yaml:"openai,omitempty"`

// configFile is the configuration file used
configFile string
}
Expand All @@ -72,6 +74,14 @@ type LogSink struct {
Path string `json:"path,omitempty" yaml:"path,omitempty"`
}

type OpenAIConfig struct {
// APIKeyFile is the path to the file containing the API key
APIKeyFile string `json:"apiKeyFile" yaml:"apiKeyFile"`

// BaseURL is the baseURL for the API.
BaseURL string `json:"baseURL" yaml:"baseURL"`
}

func (c *Config) GetLogLevel() string {
if c.Logging.Level == "" {
return "info"
Expand Down
5 changes: 2 additions & 3 deletions pkg/lists/e2etests/starterpacks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,11 @@ func Test_GetStarterPacks(t *testing.T) {
t.Skipf("Test_StarterPacks is a manual test that is skipped in CICD")
}

stuff, err := testutil.testSetup()
stuff, err := testutil.New()
if err != nil {
t.Fatalf("testSetup() = %v, wanted nil", err)
}
// SHould be Chris Albon who has a starter pack
// actor := "did:plc:umpsiyampiq3bpgce7kigydz"

actor := jeremyLewiDid

out, err := lists.GetStarterPacks(stuff.Client, actor)
Expand Down
81 changes: 81 additions & 0 deletions pkg/lists/e2etests/walker_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package e2etests

import (
"context"
"github.com/jlewi/bsctl/pkg/api/v1alpha1"
"github.com/jlewi/bsctl/pkg/lists"
"github.com/jlewi/bsctl/pkg/testutil"
"os"
"testing"
)

func Test_Walker(t *testing.T) {
if os.Getenv("GITHUB_ACTIONS") != "" {
t.Skipf("Test_AccountsListApply is a manual test that is skipped in CICD")
}

stuff, err := testutil.New()
if err != nil {
t.Fatalf("testSetup() = %v, wanted nil", err)
}

app := stuff.App
client, err := app.GetOAIClient(context.Background())
if err != nil {
t.Fatalf("Failed to create OAI client; error %+v", err)
}
w, err := lists.NewWalker(stuff.Client, client)
if err != nil {
t.Fatalf("Failed to create walker; %+v", err)
}

f, err := os.CreateTemp("", "accounts.yaml")
if err != nil {
t.Fatalf("Failed to create temp file; %+v", err)
}

if err := f.Close(); err != nil {
t.Fatalf("Failed to close file; %+v", err)
}

oFile := f.Name()

t.Logf("Output file: %s", oFile)

buildSpec := &v1alpha1.CommunityBuilder{
OutputFile: oFile,
Seeds: []v1alpha1.Account{
{
Handle: "jeremy.lewi.us",
},
},
Definition: v1alpha1.CommunityDefinition{
Name: "Platform Engineer",
Examples: []v1alpha1.ProfileExample{
{
Profile: "I'm a platform engineer at acme.co",
Member: true,
},
},
Criterion: []string{
"They are working on an internal developer platform",
"They describe their job role as platform engineer, ml platform engineer, devops, infrastructure engineer or SRE",
"They work with technologies used to build platforms; eg. kubernetes, cloud, argo",
"They describe practices central to platform engineering; e.g. IAC, configuration, containers, gitops, cicd",
},
},
}

//b, err := yaml.Marshal(buildSpec)
//if err != nil {
// t.Fatalf("Failed to marshal buildSpec; %+v", err)
//}
//if err := os.WriteFile("/tmp/platform_community_builder.yaml", b, 0644); err != nil {
// t.Fatalf("Failed to write buildSpec; %+v", err)
//}
//return

if err := w.Reconcile(context.Background(), buildSpec); err != nil {
t.Fatalf("Reconcile() = %v, wanted nil", err)
}
}
23 changes: 23 additions & 0 deletions pkg/lists/profile_prompt.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
You will be given a short description of a person taken from their social media account.
Decide whether the person belongs to the {{.Definition.Name}} community.

Use the following criterion to decide if someone belongs to the community
{{range .Definition.Criterion}}
* {{.}}{{end}}

Emit the result as a json dictionary with field member which is a boolean
which should be true if the profile belongs and false otherwise.
Also include a field explanation with a short explanation of your classification.
{{if .Definition.Examples}}
Here are a bunch of examples of input documents along with the expected output.
{{range .Definition.Examples}}
<example>
<input>
{{.Profile}}
</input>
<output>
{ "member": {{.Member}}, "explanation": "{{.Explanation}}" }
</output>
</example>{{end}}{{end}}
Here's the profile you need to classify:
{{.Profile}}
33 changes: 33 additions & 0 deletions pkg/lists/prompt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package lists

import (
_ "embed"
"github.com/jlewi/bsctl/pkg/api/v1alpha1"
"github.com/pkg/errors"
"strings"
"text/template"
)

//go:embed profile_prompt.tmpl
var promptTemplateString string

var (
promptTemplate = template.Must(template.New("prompt").Parse(promptTemplateString))
)

type PromptInput struct {
Definition v1alpha1.CommunityDefinition
Profile string
}

func buildPrompt(definition v1alpha1.CommunityDefinition, profile string) (string, error) {
var sb strings.Builder
input := PromptInput{
Definition: definition,
Profile: profile,
}
if err := promptTemplate.Execute(&sb, input); err != nil {
return "", errors.Wrapf(err, "Failed to execute prompt template")
}
return sb.String(), nil
}
Loading

0 comments on commit 9c8e1cd

Please sign in to comment.