Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial version of a sdk issue triage bot #264

Merged
merged 12 commits into from
Jun 6, 2024
129 changes: 116 additions & 13 deletions .github/workflows/dart.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions pkgs/sdk_triage_bot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# https://dart.dev/guides/libraries/private-files

# Created by `dart pub`
devoncarew marked this conversation as resolved.
Show resolved Hide resolved
.dart_tool/
pubspec.lock

.env

tool/training.csv
tool/training.jsonl
tool/training.txt
20 changes: 20 additions & 0 deletions pkgs/sdk_triage_bot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## What's this?

A LLM based triage automation system for the dart-lang/sdk repo. It processes
new issues filed against the repo and triages them in the same manner that a
human would. This includes:

- re-summarizing the issue for clarity
- assigning the issues to an `area-` label (first line triage)

## Bot trigger and entry-point

TODO: doc

## Overview

TODO: doc

## Tuning

TODO: doc
1 change: 1 addition & 0 deletions pkgs/sdk_triage_bot/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:dart_flutter_team_lints/analysis_options.yaml
83 changes: 83 additions & 0 deletions pkgs/sdk_triage_bot/bin/triage.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:io' as io;

import 'package:args/args.dart';
import 'package:github/github.dart';
import 'package:http/http.dart' as http;
import 'package:sdk_triage_bot/src/common.dart';
import 'package:sdk_triage_bot/src/gemini.dart';
import 'package:sdk_triage_bot/src/github.dart';
import 'package:sdk_triage_bot/triage.dart';

void main(List<String> arguments) async {
final argParser = ArgParser();
argParser.addFlag('dry-run',
negatable: false,
help: 'Perform triage but don\'t make any actual changes to the issue.');
argParser.addFlag('force',
negatable: false,
help: 'Make changes to the issue even if it already looks triaged.');
argParser.addFlag('help',
abbr: 'h', negatable: false, help: 'Print this usage information.');

final ArgResults results;
try {
results = argParser.parse(arguments);
} on ArgParserException catch (e) {
print(e.message);
print('');
print(usage);
print('');
print(argParser.usage);
io.exit(64);
}

if (results.flag('help') || results.rest.isEmpty) {
print(usage);
print('');
print(argParser.usage);
io.exit(results.flag('help') ? 0 : 64);
}

var issue = results.rest.first;
final dryRun = results.flag('dry-run');
final force = results.flag('force');

// Accept either an issue number or a url (i.e.,
// https://github.com/dart-lang/sdk/issues/55816).
const sdkToken = 'dart-lang/sdk/issues/';
if (issue.contains(sdkToken)) {
issue = issue.substring(issue.indexOf(sdkToken) + sdkToken.length);
}

final client = http.Client();

final github = GitHub(
auth: Authentication.withToken(githubToken),
client: client,
);
final githubService = GithubService(github: github);

final geminiService = GeminiService(
apiKey: geminiKey,
httpClient: client,
);

await triage(
int.parse(issue),
dryRun: dryRun,
force: force,
githubService: githubService,
geminiService: geminiService,
);

client.close();
}

const String usage = '''
A tool to triage issues from https://github.com/dart-lang/sdk.

usage: dart bin/triage.dart [options] <issue>''';
43 changes: 43 additions & 0 deletions pkgs/sdk_triage_bot/lib/src/common.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:io';

String? _envFileTokenOrEnvironment({required String key}) {
final envFile = File('.env');
if (envFile.existsSync()) {
final env = <String, String>{};
for (var line in envFile.readAsLinesSync().map((line) => line.trim())) {
if (line.isEmpty || line.startsWith('#')) continue;
var split = line.indexOf('=');
env[line.substring(0, split).trim()] = line.substring(split + 1).trim();
}
return env[key];
} else {
return Platform.environment[key];
}
}

String get githubToken {
var token = _envFileTokenOrEnvironment(key: 'GITHUB_TOKEN');
if (token == null) {
throw StateError('This tool expects a github access token in the '
'GITHUB_TOKEN environment variable.');
}
return token;
}

String get geminiKey {
var token = _envFileTokenOrEnvironment(key: 'GOOGLE_API_KEY');
if (token == null) {
throw StateError('This tool expects a gemini api key in the '
'GOOGLE_API_KEY environment variable.');
}
return token;
}

/// Don't return more than 4k of text for an issue body.
String trimmedBody(String body) {
return body.length > 4096 ? body = body.substring(0, 4096) : body;
}
Loading