Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions go-libs/llnotes/announce.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,28 @@ import (
"github.com/databricks/databricks-sdk-go/logger"
)

var blogPrompt = MessageTemplate(`Do not hallucinate.
You are professional technical writer and you receive draft release notes for {{.version}} of project called {{.repo.Name}} in a markdown format from multiple team members.
Project can be described as "{{.repo.Description}}"
var blogPrompt = MessageTemplate(`Role: Technical content writer for {{.repo.Name}} release announcements.

You write a long post announcement that takes at least 5 minutes to read, summarize the most important features, and mention them on top. Keep the markdown links when relevant.
Do not use headings. Write fluent paragraphs, that are at least few sentences long. Blog post title should nicely summarize the feature increments of this release.
Project: {{.repo.Description}}
Version: {{.version}}

Don't abuse lists. paragraphs should have at least 3-4 sentences. The title should be one-sentence summary of the incremental updates for this release
Task: Create a concise blog post announcing this release.

You aim at driving more adoption of the project on Medium.`)
Structure:
1. Title: One-sentence summary of key improvements (10-15 words)
2. Opening: Lead with the most impactful feature (2-3 sentences)
3. Key Changes: 3-5 paragraphs, each covering one major feature area
4. Summary: Brief closing with call-to-action (1-2 sentences)

Requirements:
- Target length: 400-600 words (3-4 minute read)
- Each paragraph: 3-5 sentences focused on user benefits
- Use bullet points sparingly (max 1 list if essential)
- Maintain technical accuracy while being accessible
- Include markdown links from original notes
- Emphasize adoption value and practical use cases

Tone: Professional, enthusiastic, developer-focused`)

func (lln *llNotes) versionNotes(ctx context.Context, newVersion string) ([]string, error) {
versions, err := listing.ToSlice(ctx, lln.gh.Versions(ctx, lln.org, lln.repo))
Expand Down
23 changes: 15 additions & 8 deletions go-libs/llnotes/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,21 @@ import (
"github.com/sourcegraph/go-diff/diff"
)

const reduceDiffPrompt = `Do not hallucinate.
You are a professional Technical Writer writing feature change description for the open-source library.
Do not use file names, because they are not relevant for the feature description.
Do not use phrases like "In this release".
Your target audience is software engineers.
You receive a change description from your software engineering team about the newly developed features.
Write a one-paragraph summary of this change for the release notes.
It has to be one paragraph of text, because it will be included in a bigger document.`
const reduceDiffPrompt = `Role: Professional technical writer creating release notes.

Task: Consolidate these change descriptions into a single, cohesive changelog entry.

Requirements:
- Maximum 50 words
- One paragraph only
- Highlight the primary user benefit or feature
- Use active voice and past tense
- Group related changes into themes
- Omit technical jargon and implementation details

Output format: <Primary action> <feature area>: <value delivered>. <Optional: secondary changes if critical>.

Example: "Enhanced authentication system with OAuth2 support and session management, improving security and user experience. Added rate limiting to prevent abuse."`

func (lln *llNotes) explainDiff(ctx context.Context, history History, buf *bytes.Buffer) (History, error) {
prDiff, err := diff.ParseMultiFileDiff(buf.Bytes())
Expand Down
27 changes: 15 additions & 12 deletions go-libs/llnotes/pull_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,22 @@ import (
"github.com/databrickslabs/sandbox/go-libs/github"
)

var fileDiffTemplate = MessageTemplate(`Here is the commit message terminated by --- for the context:
{{.Message}}
---
var fileDiffTemplate = MessageTemplate(`Context: {{.Message}}

Do not hallucinate.
You are Staff Software Engineer, and you are reviewing one file at a time in a unitified diff format.
Do not use phrases like "In this diff", "In this pull request", or "In this file".
Do not mention file names, because they are not relevant for the feature description.
If new methods are added, explain what these methods are doing.
If existing funcitonality is changed, explain the scope of these changes.
Please summarize the input as a signle paragraph of text written in American English.
Your target audience is software engineers, who adopt your project.
If the prompt contains ordered or unordered lists, rewrite the entire response as a paragraph of text.`)
Role: You are a senior software engineer writing changelog entries.

Task: Analyze this unified diff and write a concise changelog entry.

Requirements:
- Write 1-2 sentences maximum (20-40 words)
- Focus on user-facing impact and behavior changes
- Omit file names, implementation details, and meta-commentary
- Use past tense (e.g., "Added", "Fixed", "Updated")
- Target audience: developers using this library

Format: <Action> <feature/component>: <concise description of change>

Example: "Added support for custom validators in form fields, enabling client-side validation before submission."`)

func (lln *llNotes) CommitBySHA(ctx context.Context, sha string) (History, error) {
commit, err := lln.gh.GetCommit(ctx, lln.org, lln.repo, sha)
Expand Down
25 changes: 20 additions & 5 deletions go-libs/llnotes/talk.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ import (
"github.com/databrickslabs/sandbox/go-libs/sed"
)

type contextKey string

const maxTokensKey contextKey = "maxTokens"

// WithMaxTokens returns a new context with the specified maxTokens value
func WithMaxTokens(ctx context.Context, maxTokens int) context.Context {
return context.WithValue(ctx, maxTokensKey, maxTokens)
}

type Settings struct {
GitHub github.GitHubConfig
Databricks config.Config
Expand All @@ -34,7 +43,7 @@ func New(cfg *Settings) (*llNotes, error) {
cfg.Model = "databricks-meta-llama-3-3-70b-instruct"
}
if cfg.MaxTokens == 0 {
cfg.MaxTokens = 4000
cfg.MaxTokens = 2048 // Reduced from 4000 for crisper output
}
if cfg.Workers == 0 {
cfg.Workers = 15
Expand Down Expand Up @@ -72,11 +81,17 @@ type llNotes struct {
}

func (lln *llNotes) Talk(ctx context.Context, h History) (History, error) {
logger.Debugf(ctx, "Talking with AI:\n%s", h.Excerpt(80))
// Read maxTokens from context, fallback to config default
maxTokens := lln.cfg.MaxTokens
if ctxMaxTokens, ok := ctx.Value(maxTokensKey).(int); ok && ctxMaxTokens > 0 {
maxTokens = ctxMaxTokens
}
logger.Debugf(ctx, "Talking with AI (max tokens: %d):\n%s", maxTokens, h.Excerpt(80))
response, err := lln.w.ServingEndpoints.Query(ctx, serving.QueryEndpointInput{
Name: lln.model,
Messages: h.Messages(),
MaxTokens: lln.cfg.MaxTokens,
Name: lln.model,
Messages: h.Messages(),
MaxTokens: maxTokens,
Temperature: 0.1,
})
if err != nil {
return nil, fmt.Errorf("llm: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion go.work
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
go 1.24
go 1.24.0

toolchain go1.24.2

Expand Down
5 changes: 5 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,7 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
Expand Down Expand Up @@ -1227,6 +1228,8 @@ golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down Expand Up @@ -1338,6 +1341,7 @@ golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXct
golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk=
golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4=
golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjhjHsTNdVEEMZNWA0quBnfrO+AfoDSAKw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
Expand Down Expand Up @@ -1428,6 +1432,7 @@ golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
122 changes: 122 additions & 0 deletions llnotes/CLAUDE.md
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's better to call it AGENTS.md and create CLAUDE.md symlink - in this case it will work with more LLMs

Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

**llnotes** is a CLI tool that generates GitHub release notes using LLMs hosted on Databricks Model Serving. It's part of the databrickslabs/sandbox monorepo and uses the shared `go-libs` library for common functionality.

## Monorepo Structure

This project exists within a Go workspace monorepo (`../go.work`). Key locations:
- **Application code**: `./llnotes/` (this directory)
- **Shared libraries**: `../go-libs/` - contains reusable packages including:
- `llnotes`: Core business logic for release notes generation
- `lite`: CLI framework for building commands
- `github`: GitHub API integration utilities
- `git`: Git operations helpers
- **Other projects**: `../acceptance/`, `../metascan/`, etc.

## Build & Development Commands

```bash
# Build the project (default target)
make

# Run linting
make lint

# Run tests
make test

# View test coverage in browser
make coverage

# Format code
make fmt

# Update dependencies and vendor folder
make vendor
```

**Important**: This project uses `go work vendor` (not `go mod vendor`) because it's part of a Go workspace. Always run `make vendor` instead of `go mod vendor`.

## Running the Application

### Authentication Setup

Before using llnotes, authenticate with both services:

```bash
# Databricks authentication
databricks auth login https://....cloud.databricks.com/

# GitHub authentication
gh auth login
```

### Available Commands

```bash
# Generate pull request description
llnotes pull-request --number <PR_NUMBER>

# Generate release notes for upcoming release
llnotes upcoming-release

# Generate release notes between two git references
llnotes diff --since <TAG_OR_COMMIT> --until <TAG_OR_COMMIT>

# Generate release announcement
llnotes announce --version <VERSION>
```

### Configuration Flags

Common flags available across commands:
- `--model`: Serving chat model (default: "databricks-claude-sonnet-4-5")
- `--org`: GitHub organization (default: "databrickslabs")
- `--repo`: GitHub repository (default: "ucx")
- `--profile`: Databricks config profile
- GitHub authentication: `--github-token`, `--github-app-id`, `--github-app-installation-id`, etc.

Config is stored in `$HOME/.databricks/labs/llnotes/`

## Architecture

### Command Structure

The application uses the `lite` framework (from `go-libs/lite`) for CLI scaffolding. Commands are registered in `main.go`:
- Each command follows the `lite.Command` pattern with `Name`, `Short`, `Flags`, and `Run` functions
- Commands interact with the core `llnotes` library (`../go-libs/llnotes/`)
- The `askFor()` helper provides interactive prompts for iterative refinement

### Core Library (go-libs/llnotes)

The business logic lives in `../go-libs/llnotes/`:
- `pull_request.go`: PR description generation
- `release_notes.go`: Release notes from commits
- `diff.go`: Diff-based release notes
- `announce.go`: Release announcements
- `talk.go`: Interactive chat with LLM
- `chain.go`: Message chain management

### Key Design Patterns

1. **Conversation Chain**: Uses a message chain pattern (`chain.go`) to maintain context across LLM interactions
2. **Interactive Mode**: `pull-request` and `announce` commands support iterative refinement through user prompts
3. **Settings Configuration**: Central `Settings` struct manages GitHub and Databricks credentials, model selection, and repo details

## Dependencies

- `github.com/databricks/databricks-sdk-go`: Databricks API SDK for model serving
- `github.com/databrickslabs/sandbox/go-libs`: Shared monorepo libraries
- `github.com/spf13/pflag`: CLI flag parsing
- `github.com/fatih/color`: Terminal color output

## Testing

Tests use the standard Go testing framework with `gotestsum` for better output formatting:
- Test files would follow `*_test.go` naming convention
- Run with `make test` (includes coverage output to `coverage.txt`)
- Use `-short` flag to skip long-running tests
2 changes: 1 addition & 1 deletion llnotes/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func main() {
flags.StringVar(&cfg.GitHub.PrivateKeyPath, "github-app-private-key-path", "", "GitHub App Private Key file (*.pem) path")
flags.StringVar(&cfg.GitHub.PrivateKeyBase64, "github-app-private-key", "", "GitHub App Private Key encoded in base64")
flags.StringVar(&cfg.Databricks.Profile, "profile", "", "Databricks config profile")
flags.StringVar(&cfg.Model, "model", "databricks-meta-llama-3-3-70b-instruct", "Serving chat model")
flags.StringVar(&cfg.Model, "model", "databricks-claude-sonnet-4-5", "Serving chat model")
flags.StringVar(&cfg.Org, "org", "databrickslabs", "GitHub org")
flags.StringVar(&cfg.Repo, "repo", "ucx", "GitHub repository")
},
Expand Down
Loading