diff --git a/.github/workflows/ai.yml b/.github/workflows/ai.yml new file mode 100644 index 000000000..1455e8b69 --- /dev/null +++ b/.github/workflows/ai.yml @@ -0,0 +1,56 @@ +# auto-generated by scripts/builds. DO NOT EDIT. +name: ai + +on: + push: + branches: + - main + pull_request: + paths: + - ai/** + - .github/workflows/ai.yml + +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + defaults: + run: + working-directory: ./ai + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./ai/go.mod + cache: true + cache-dependency-path: ./ai/go.sum + - run: go build -v ./... + - run: go test -race -v ./... + + dependabot: + needs: [build] + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: write + if: ${{ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request'}} + steps: + - id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - run: | + gh pr review --approve "$PR_URL" + gh pr merge --squash --auto "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + lint: + uses: charmbracelet/meta/.github/workflows/lint.yml@main + with: + directory: ./ai/... + golangci_path: .golangci.yml + diff --git a/.github/workflows/anthropic.yml b/.github/workflows/anthropic.yml new file mode 100644 index 000000000..7779163ce --- /dev/null +++ b/.github/workflows/anthropic.yml @@ -0,0 +1,56 @@ +# auto-generated by scripts/builds. DO NOT EDIT. +name: anthropic + +on: + push: + branches: + - main + pull_request: + paths: + - providers/anthropic/** + - .github/workflows/anthropic.yml + +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + defaults: + run: + working-directory: ./providers/anthropic + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./providers/anthropic/go.mod + cache: true + cache-dependency-path: ./providers/anthropic/go.sum + - run: go build -v ./... + - run: go test -race -v ./... + + dependabot: + needs: [build] + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: write + if: ${{ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request'}} + steps: + - id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - run: | + gh pr review --approve "$PR_URL" + gh pr merge --squash --auto "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + lint: + uses: charmbracelet/meta/.github/workflows/lint.yml@main + with: + directory: ./providers/anthropic/... + golangci_path: .golangci.yml + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 3511f7fe0..000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: build -on: [push, pull_request] - -jobs: - build: - uses: charmbracelet/meta/.github/workflows/build.yml@main - with: - go-version: "" - go-version-file: ./go.mod - secrets: - gh_pat: "${{ secrets.PERSONAL_ACCESS_TOKEN }}" diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml new file mode 100644 index 000000000..273aa481f --- /dev/null +++ b/.github/workflows/examples.yml @@ -0,0 +1,56 @@ +# auto-generated by scripts/builds. DO NOT EDIT. +name: examples + +on: + push: + branches: + - main + pull_request: + paths: + - examples/** + - .github/workflows/examples.yml + +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + defaults: + run: + working-directory: ./examples + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./examples/go.mod + cache: true + cache-dependency-path: ./examples/go.sum + - run: go build -v ./... + - run: go test -race -v ./... + + dependabot: + needs: [build] + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: write + if: ${{ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request'}} + steps: + - id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - run: | + gh pr review --approve "$PR_URL" + gh pr merge --squash --auto "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + lint: + uses: charmbracelet/meta/.github/workflows/lint.yml@main + with: + directory: ./examples/... + golangci_path: .golangci.yml + diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index 1ffad5f2f..000000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: lint -on: - push: - pull_request: - -jobs: - lint: - uses: charmbracelet/meta/.github/workflows/lint.yml@main - with: - golangci_path: .golangci.yml - timeout: 10m diff --git a/.github/workflows/openai.yml b/.github/workflows/openai.yml new file mode 100644 index 000000000..5aa12b290 --- /dev/null +++ b/.github/workflows/openai.yml @@ -0,0 +1,56 @@ +# auto-generated by scripts/builds. DO NOT EDIT. +name: openai + +on: + push: + branches: + - main + pull_request: + paths: + - providers/openai/** + - .github/workflows/openai.yml + +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + defaults: + run: + working-directory: ./providers/openai + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: ./providers/openai/go.mod + cache: true + cache-dependency-path: ./providers/openai/go.sum + - run: go build -v ./... + - run: go test -race -v ./... + + dependabot: + needs: [build] + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: write + if: ${{ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request'}} + steps: + - id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - run: | + gh pr review --approve "$PR_URL" + gh pr merge --squash --auto "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + lint: + uses: charmbracelet/meta/.github/workflows/lint.yml@main + with: + directory: ./providers/openai/... + golangci_path: .golangci.yml + diff --git a/.gitignore b/.gitignore index 5eb364326..02ef00589 100644 --- a/.gitignore +++ b/.gitignore @@ -14,10 +14,6 @@ # Dependency directories (remove the comment below to include it) vendor/ -# Go workspace file -go.work -go.work.sum - # IDE specific files .idea/ .vscode/ diff --git a/.golangci.yml b/.golangci.yml index 136f67403..527a15c34 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -7,7 +7,7 @@ linters: # - exhaustive # - goconst - godot - - gomoddirectives + # - gomoddirectives - goprintffuncname - gosec - misspell diff --git a/Taskfile.yaml b/Taskfile.yaml index 82864bf8e..e428c1dfd 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -1,23 +1,36 @@ version: '3' +env: + GOPRIVATE: github.com/charmbracelet/* + +vars: + PACKAGES: + - ./ai + - ./examples + - ./providers/anthropic + - ./providers/openai tasks: - test: - desc: Run tests + fmt: + desc: Run gofumpt for all packages cmds: - - go test ./... + - for: { var: PACKAGES } + cmd: cd {{.ITEM}} && gofmt -s -w . - lint: - desc: Run linters + modernize: + desc: Run modernize for all packages cmds: - - golangci-lint run --config .golangci.yml --timeout 10m + - for: { var: PACKAGES } + cmd: cd {{.ITEM}} && modernize -fix ./... - lint:fix: - desc: Fix lint issues + lint: + desc: Run base linters for all packages cmds: - - golangci-lint run --config .golangci.yml --fix + - for: { var: PACKAGES } + cmd: cd {{.ITEM}} && golangci-lint run - fmt: - desc: Format code + test: + desc: Run tests for all packages cmds: - - gofumpt -w . + - for: { var: PACKAGES } + cmd: cd {{.ITEM}} && go test ./... {{.CLI_ARGS}} diff --git a/agent.go b/ai/agent.go similarity index 100% rename from agent.go rename to ai/agent.go diff --git a/agent_stream_test.go b/ai/agent_stream_test.go similarity index 100% rename from agent_stream_test.go rename to ai/agent_stream_test.go diff --git a/agent_test.go b/ai/agent_test.go similarity index 100% rename from agent_test.go rename to ai/agent_test.go diff --git a/content.go b/ai/content.go similarity index 100% rename from content.go rename to ai/content.go diff --git a/errors.go b/ai/errors.go similarity index 100% rename from errors.go rename to ai/errors.go diff --git a/ai/go.mod b/ai/go.mod new file mode 100644 index 000000000..783293b00 --- /dev/null +++ b/ai/go.mod @@ -0,0 +1,14 @@ +module github.com/charmbracelet/ai/ai + +go 1.24.5 + +require ( + github.com/go-viper/mapstructure/v2 v2.4.0 + github.com/stretchr/testify v1.11.1 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/ai/go.sum b/ai/go.sum new file mode 100644 index 000000000..5dc0e5c95 --- /dev/null +++ b/ai/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +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/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/model.go b/ai/model.go similarity index 100% rename from model.go rename to ai/model.go diff --git a/provider.go b/ai/provider.go similarity index 100% rename from provider.go rename to ai/provider.go diff --git a/retry.go b/ai/retry.go similarity index 100% rename from retry.go rename to ai/retry.go diff --git a/tool.go b/ai/tool.go similarity index 100% rename from tool.go rename to ai/tool.go diff --git a/tool_test.go b/ai/tool_test.go similarity index 100% rename from tool_test.go rename to ai/tool_test.go diff --git a/util.go b/ai/util.go similarity index 100% rename from util.go rename to ai/util.go diff --git a/examples/agent/main.go b/examples/agent/main.go index 97364783f..c91ba5868 100644 --- a/examples/agent/main.go +++ b/examples/agent/main.go @@ -5,7 +5,7 @@ import ( "fmt" "os" - "github.com/charmbracelet/ai" + "github.com/charmbracelet/ai/ai" "github.com/charmbracelet/ai/providers/openai" ) diff --git a/examples/go.mod b/examples/go.mod new file mode 100644 index 000000000..aaabfa161 --- /dev/null +++ b/examples/go.mod @@ -0,0 +1,26 @@ +module github.com/charmbracelet/ai/examples + +go 1.24.5 + +require ( + github.com/charmbracelet/ai/ai v0.0.0-00010101000000-000000000000 + github.com/charmbracelet/ai/providers/anthropic v0.0.0-00010101000000-000000000000 + github.com/charmbracelet/ai/providers/openai v0.0.0-00010101000000-000000000000 +) + +require ( + github.com/anthropics/anthropic-sdk-go v1.10.0 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/openai/openai-go/v2 v2.2.1 // indirect + github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/tidwall/sjson v1.2.5 // indirect +) + +replace github.com/charmbracelet/ai/ai => ../ai + +replace github.com/charmbracelet/ai/providers/anthropic => ../providers/anthropic + +replace github.com/charmbracelet/ai/providers/openai => ../providers/openai diff --git a/examples/go.sum b/examples/go.sum new file mode 100644 index 000000000..c33818589 --- /dev/null +++ b/examples/go.sum @@ -0,0 +1,26 @@ +github.com/anthropics/anthropic-sdk-go v1.10.0 h1:jDKQTfC0miIEj21eMmPrNSLKTNdNa3nHZOhd4wZz1cI= +github.com/anthropics/anthropic-sdk-go v1.10.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/openai/openai-go/v2 v2.2.1 h1:rixFJaRnxFqOhXk/0PdtMearOTVf3s17jEXe091YE+M= +github.com/openai/openai-go/v2 v2.2.1/go.mod h1:sIUkR+Cu/PMUVkSKhkk742PRURkQOCFhiwJ7eRSBqmk= +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/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/examples/simple/main.go b/examples/simple/main.go index 5aea35047..b3f950e1c 100644 --- a/examples/simple/main.go +++ b/examples/simple/main.go @@ -5,7 +5,7 @@ import ( "fmt" "os" - "github.com/charmbracelet/ai" + "github.com/charmbracelet/ai/ai" "github.com/charmbracelet/ai/providers/anthropic" ) diff --git a/examples/stream/main.go b/examples/stream/main.go index 1eb3c9ee1..f141594d5 100644 --- a/examples/stream/main.go +++ b/examples/stream/main.go @@ -6,7 +6,7 @@ import ( "fmt" "os" - "github.com/charmbracelet/ai" + "github.com/charmbracelet/ai/ai" "github.com/charmbracelet/ai/providers/openai" ) diff --git a/examples/streaming-agent-simple/main.go b/examples/streaming-agent-simple/main.go index 5f94d0a30..46dbb041e 100644 --- a/examples/streaming-agent-simple/main.go +++ b/examples/streaming-agent-simple/main.go @@ -5,7 +5,7 @@ import ( "fmt" "os" - "github.com/charmbracelet/ai" + "github.com/charmbracelet/ai/ai" "github.com/charmbracelet/ai/providers/openai" ) diff --git a/examples/streaming-agent/main.go b/examples/streaming-agent/main.go index fb4fa4005..f009753e4 100644 --- a/examples/streaming-agent/main.go +++ b/examples/streaming-agent/main.go @@ -6,7 +6,7 @@ import ( "os" "strings" - "github.com/charmbracelet/ai" + "github.com/charmbracelet/ai/ai" "github.com/charmbracelet/ai/providers/anthropic" ) diff --git a/go.work b/go.work new file mode 100644 index 000000000..a8728af5f --- /dev/null +++ b/go.work @@ -0,0 +1,8 @@ +go 1.24.5 + +use ( + ./ai + ./examples + ./providers/anthropic + ./providers/openai +) diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 000000000..71775dd44 --- /dev/null +++ b/go.work.sum @@ -0,0 +1,50 @@ +cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs= +cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= +github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= +github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= diff --git a/providers/anthropic/anthropic.go b/providers/anthropic/anthropic.go index b2d089da1..88a322e97 100644 --- a/providers/anthropic/anthropic.go +++ b/providers/anthropic/anthropic.go @@ -14,7 +14,7 @@ import ( "github.com/anthropics/anthropic-sdk-go" "github.com/anthropics/anthropic-sdk-go/option" "github.com/anthropics/anthropic-sdk-go/packages/param" - "github.com/charmbracelet/ai" + "github.com/charmbracelet/ai/ai" ) type options struct { diff --git a/providers/anthropic/go.mod b/providers/anthropic/go.mod new file mode 100644 index 000000000..774383a35 --- /dev/null +++ b/providers/anthropic/go.mod @@ -0,0 +1,18 @@ +module github.com/charmbracelet/ai/providers/anthropic + +go 1.24.5 + +require ( + github.com/anthropics/anthropic-sdk-go v1.10.0 + github.com/charmbracelet/ai/ai v0.0.0-00010101000000-000000000000 +) + +require ( + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/tidwall/sjson v1.2.5 // indirect +) + +replace github.com/charmbracelet/ai/ai => ../../ai diff --git a/providers/anthropic/go.sum b/providers/anthropic/go.sum new file mode 100644 index 000000000..c2d26e042 --- /dev/null +++ b/providers/anthropic/go.sum @@ -0,0 +1,22 @@ +github.com/anthropics/anthropic-sdk-go v1.10.0 h1:jDKQTfC0miIEj21eMmPrNSLKTNdNa3nHZOhd4wZz1cI= +github.com/anthropics/anthropic-sdk-go v1.10.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +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/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go.mod b/providers/openai/go.mod similarity index 61% rename from go.mod rename to providers/openai/go.mod index cd77a1476..aa3b70b42 100644 --- a/go.mod +++ b/providers/openai/go.mod @@ -1,17 +1,17 @@ -module github.com/charmbracelet/ai +module github.com/charmbracelet/ai/providers/openai go 1.24.5 require ( - github.com/anthropics/anthropic-sdk-go v1.9.1 - github.com/go-viper/mapstructure/v2 v2.4.0 + github.com/charmbracelet/ai/ai v0.0.0-00010101000000-000000000000 github.com/google/uuid v1.6.0 - github.com/openai/openai-go/v2 v2.1.1 + github.com/openai/openai-go/v2 v2.2.1 github.com/stretchr/testify v1.11.1 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -19,3 +19,5 @@ require ( github.com/tidwall/sjson v1.2.5 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/charmbracelet/ai/ai => ../../ai diff --git a/go.sum b/providers/openai/go.sum similarity index 82% rename from go.sum rename to providers/openai/go.sum index 2e53e3c1b..9086ebefd 100644 --- a/go.sum +++ b/providers/openai/go.sum @@ -1,20 +1,17 @@ -github.com/anthropics/anthropic-sdk-go v1.9.1 h1:raRhZKmayVSVZtLpLDd6IsMXvxLeeSU03/2IBTerWlg= -github.com/anthropics/anthropic-sdk-go v1.9.1/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/openai/openai-go/v2 v2.1.1 h1:/RMA/V3D+yF/Cc4jHXFt6lkqSOWRf5roRi+DvZaDYQI= -github.com/openai/openai-go/v2 v2.1.1/go.mod h1:sIUkR+Cu/PMUVkSKhkk742PRURkQOCFhiwJ7eRSBqmk= +github.com/openai/openai-go/v2 v2.2.1 h1:rixFJaRnxFqOhXk/0PdtMearOTVf3s17jEXe091YE+M= +github.com/openai/openai-go/v2 v2.2.1/go.mod h1:sIUkR+Cu/PMUVkSKhkk742PRURkQOCFhiwJ7eRSBqmk= 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/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= -github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= diff --git a/internal/jsonext/json.go b/providers/openai/json.go similarity index 71% rename from internal/jsonext/json.go rename to providers/openai/json.go index 467f22e2c..0601aa0c3 100644 --- a/internal/jsonext/json.go +++ b/providers/openai/json.go @@ -1,10 +1,10 @@ -package jsonext +package openai import ( "encoding/json" ) -func IsValidJSON[T string | []byte](data T) bool { +func isValidJSON[T string | []byte](data T) bool { if len(data) == 0 { // hot path return false } diff --git a/providers/openai/openai.go b/providers/openai/openai.go index 3c27222a1..c62a8da3c 100644 --- a/providers/openai/openai.go +++ b/providers/openai/openai.go @@ -11,8 +11,7 @@ import ( "maps" "strings" - "github.com/charmbracelet/ai" - "github.com/charmbracelet/ai/internal/jsonext" + "github.com/charmbracelet/ai/ai" "github.com/google/uuid" "github.com/openai/openai-go/v2" "github.com/openai/openai-go/v2/option" @@ -585,7 +584,7 @@ func (o languageModel) Stream(ctx context.Context, call ai.Call) (ai.StreamRespo return } toolCalls[toolCallDelta.Index] = existingToolCall - if jsonext.IsValidJSON(existingToolCall.arguments) { + if isValidJSON(existingToolCall.arguments) { if !yield(ai.StreamPart{ Type: ai.StreamPartTypeToolInputEnd, ID: existingToolCall.id, @@ -646,7 +645,7 @@ func (o languageModel) Stream(ctx context.Context, call ai.Call) (ai.StreamRespo }) { return } - if jsonext.IsValidJSON(toolCalls[toolCallDelta.Index].arguments) { + if isValidJSON(toolCalls[toolCallDelta.Index].arguments) { if !yield(ai.StreamPart{ Type: ai.StreamPartTypeToolInputEnd, ID: toolCallDelta.ID, diff --git a/providers/openai/openai_test.go b/providers/openai/openai_test.go index a0c9ae9e9..5c05141d4 100644 --- a/providers/openai/openai_test.go +++ b/providers/openai/openai_test.go @@ -10,7 +10,7 @@ import ( "strings" "testing" - "github.com/charmbracelet/ai" + "github.com/charmbracelet/ai/ai" "github.com/openai/openai-go/v2/packages/param" "github.com/stretchr/testify/require" ) diff --git a/scripts/generate_ci b/scripts/generate_ci new file mode 100755 index 000000000..b74c6542a --- /dev/null +++ b/scripts/generate_ci @@ -0,0 +1,71 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC2016 +find . -type f -name go.mod | sort | while read -r mod; do + dir="$(dirname "$mod")" + name="$(basename "$dir")" + # get the parent directory when the module is nested semver-style + if [[ "$name" =~ ^v[0-9]+.*$ ]]; then + ver="$(basename "$dir")" + dir="$(dirname "$dir")" + name="$(basename "$dir")-$ver" + fi + + sum="$dir/go.sum" + echo "# auto-generated by scripts/builds. DO NOT EDIT. +name: $name + +on: + push: + branches: + - main + pull_request: + paths: + - $(dirname "$mod" | cut -f2- -d/)/** + - .github/workflows/${name}.yml + +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: \${{ matrix.os }} + defaults: + run: + working-directory: $(dirname "$mod") + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: $mod + cache: true + cache-dependency-path: $sum + - run: go build -v ./... + - run: go test -race -v ./... + + dependabot: + needs: [build] + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: write + if: \${{ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request'}} + steps: + - id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: \"\${{ secrets.GITHUB_TOKEN }}\" + - run: | + gh pr review --approve \"\$PR_URL\" + gh pr merge --squash --auto \"\$PR_URL\" + env: + PR_URL: \${{github.event.pull_request.html_url}} + GITHUB_TOKEN: \${{secrets.GITHUB_TOKEN}} + + lint: + uses: charmbracelet/meta/.github/workflows/lint.yml@main + with: + directory: $(dirname "$mod")/... + golangci_path: .golangci.yml +" >"./.github/workflows/${name}.yml" +done