@@ -6,50 +6,25 @@ use std::io::Write;
66use std:: path:: Path ;
77use std:: process:: { Command , Stdio } ;
88
9- use crate :: llm:: LargeLanguageModel ;
109use crate :: templates:: { self , Updates , UpdatesGoal } ;
1110use rust_project_goals:: gh:: issues:: ExistingGithubIssue ;
1211use rust_project_goals:: gh:: {
1312 issue_id:: { IssueId , Repository } ,
1413 issues:: { checkboxes, list_issues_in_milestone, ExistingGithubComment } ,
1514} ;
1615
17- const QUICK_UPDATES : & [ & str ] = & [
18- "Jack and Jill went up the hill" ,
19- "To fetch a pail of water" ,
20- "Jack fell down and broke his crown" ,
21- "And Jill came tumbling after." ,
22- "Up Jack got and home did trot," ,
23- "As fast as he could caper;" ,
24- "Went to bed to mend his head" ,
25- "With vinegar and brown paper." ,
26- "Jill came in and she did grin" ,
27- "To see his paper plaster;" ,
28- "Mother, vex’d, did whip her next" ,
29- "For causing Jack's disaster." ,
30- ] ;
31-
32- fn comments_forever ( ) -> impl Iterator < Item = & ' static str > {
33- QUICK_UPDATES . iter ( ) . copied ( ) . cycle ( )
34- }
35-
3616pub async fn updates (
3717 repository : & Repository ,
3818 milestone : & str ,
3919 output_file : Option < & Path > ,
4020 start_date : & Option < NaiveDate > ,
4121 end_date : & Option < NaiveDate > ,
42- quick : bool ,
4322 vscode : bool ,
44- model_id : Option < & str > ,
45- region : Option < & str > ,
4623) -> anyhow:: Result < ( ) > {
4724 if output_file. is_none ( ) && !vscode {
4825 anyhow:: bail!( "either `--output-file` or `--vscode` must be specified" ) ;
4926 }
5027
51- let llm = LargeLanguageModel :: new ( model_id, region) . await ?;
52-
5328 let issues = list_issues_in_milestone ( repository, milestone) ?;
5429
5530 let filter = Filter {
@@ -69,14 +44,10 @@ pub async fn updates(
6944
7045 let mut updates = templates:: Updates {
7146 milestone : milestone. to_string ( ) ,
72- flagship_goals : vec ! [ ] ,
73- other_goals_with_updates : vec ! [ ] ,
74- other_goals_without_updates : vec ! [ ] ,
47+ flagship_goals : prepare_goals ( repository, & issues, & filter, true ) . await ?,
48+ other_goals : prepare_goals ( repository, & issues, & filter, false ) . await ?,
7549 } ;
7650
77- prepare_flagship_goals ( repository, & issues, & filter, & llm, quick, & mut updates) . await ?;
78- prepare_other_goals ( repository, & issues, & filter, & llm, quick, & mut updates) . await ?;
79-
8051 progress_bar:: finalize_progress_bar ( ) ;
8152
8253 // Render the output using handlebars and write it to the file.
@@ -108,17 +79,16 @@ pub async fn updates(
10879 Ok ( ( ) )
10980}
11081
111- async fn prepare_flagship_goals (
82+ async fn prepare_goals (
11283 repository : & Repository ,
11384 issues : & [ ExistingGithubIssue ] ,
11485 filter : & Filter < ' _ > ,
115- llm : & LargeLanguageModel ,
116- quick : bool ,
117- updates : & mut Updates ,
118- ) -> anyhow:: Result < ( ) > {
119- // First process the flagship goals, for which we capture the full text of comments.
86+ flagship : bool ,
87+ ) -> anyhow:: Result < Vec < UpdatesGoal > > {
88+ let mut result = vec ! [ ] ;
89+ // We process flagship and regular goals in two passes, and capture comments differently for flagship goals.
12090 for issue in issues {
121- if ! issue. has_flagship_label ( ) {
91+ if flagship != issue. has_flagship_label ( ) {
12292 continue ;
12393 }
12494
@@ -135,34 +105,9 @@ async fn prepare_flagship_goals(
135105
136106 let mut comments = issue. comments . clone ( ) ;
137107 comments. sort_by_key ( |c| c. created_at . clone ( ) ) ;
138- comments. retain ( |c| filter. matches ( c) ) ;
108+ comments. retain ( |c| !c . is_automated_comment ( ) && filter. matches ( c) ) ;
139109
140- let summary: String = if comments. len ( ) == 0 {
141- format ! ( "No updates in this period." )
142- } else if quick {
143- QUICK_UPDATES . iter ( ) . copied ( ) . collect ( )
144- } else {
145- let prompt = format ! (
146- "The following comments are updates to a project goal entitled '{title}'. \
147- The goal is assigned to {people} ({assignees}). \
148- Summarize the major developments, writing for general Rust users. \
149- Write the update in the third person and do not use pronouns when referring to people. \
150- Do not respond with anything but the summary paragraphs. \
151- ",
152- people = if issue. assignees. len( ) == 1 {
153- "1 person" . to_string( )
154- } else {
155- format!( "{} people" , issue. assignees. len( ) )
156- } ,
157- assignees = comma( & issue. assignees) ,
158- ) ;
159- let updates: String = comments. iter ( ) . map ( |c| format ! ( "\n {}\n " , c. body) ) . collect ( ) ;
160- llm. query ( & prompt, & updates)
161- . await
162- . with_context ( || format ! ( "making request to LLM failed" ) ) ?
163- } ;
164-
165- updates. flagship_goals . push ( UpdatesGoal {
110+ result. push ( UpdatesGoal {
166111 title : title. clone ( ) ,
167112 issue_number : issue. number ,
168113 issue_assignees : comma ( & issue. assignees ) ,
@@ -173,96 +118,13 @@ async fn prepare_flagship_goals(
173118 . url ( ) ,
174119 progress,
175120 is_closed : issue. state == GithubIssueState :: Closed ,
176- updates_markdown : summary,
121+ num_comments : comments. len ( ) ,
122+ comments,
177123 } ) ;
178124
179125 progress_bar:: inc_progress_bar ( ) ;
180126 }
181- Ok ( ( ) )
182- }
183-
184- async fn prepare_other_goals (
185- repository : & Repository ,
186- issues : & [ ExistingGithubIssue ] ,
187- filter : & Filter < ' _ > ,
188- llm : & LargeLanguageModel ,
189- quick : bool ,
190- updates : & mut Updates ,
191- ) -> anyhow:: Result < ( ) > {
192- // Next process the remaining goals, for which we generate a summary using an LLVM.
193- let mut quick_comments = comments_forever ( ) ;
194- for issue in issues {
195- if issue. has_flagship_label ( ) {
196- continue ;
197- }
198-
199- let title = & issue. title ;
200-
201- progress_bar:: print_progress_bar_info (
202- & format ! ( "Issue #{number}" , number = issue. number) ,
203- title,
204- progress_bar:: Color :: Green ,
205- progress_bar:: Style :: Bold ,
206- ) ;
207-
208- // Find the relevant updates that have occurred.
209- let mut comments = issue. comments . clone ( ) ;
210- comments. sort_by_key ( |c| c. created_at . clone ( ) ) ;
211- comments. retain ( |c| filter. matches ( c) ) ;
212-
213- // Use an LLM to summarize the updates.
214- let summary = if comments. len ( ) == 0 {
215- format ! ( "* No updates in this period." )
216- } else if quick {
217- let num_comments = std:: cmp:: min ( comments. len ( ) , 3 ) ;
218- quick_comments
219- . by_ref ( )
220- . take ( num_comments)
221- . map ( |c| format ! ( "* {c}\n " ) )
222- . collect ( )
223- } else {
224- let prompt = format ! (
225- "The following comments are updates to a project goal entitled '{title}'. \
226- The goal is assigned to {people} ({assignees}). \
227- Summarize the updates with a list of one or two bullet points, each one sentence. \
228- Write the update in the third person and do not use pronouns when referring to people. \
229- Format the bullet points as markdown with each bullet point beginning with `* `. \
230- Do not respond with anything but the bullet points. \
231- ",
232- people = if issue. assignees. len( ) == 1 {
233- "1 person" . to_string( )
234- } else {
235- format!( "{} people" , issue. assignees. len( ) )
236- } ,
237- assignees = comma( & issue. assignees) ,
238- ) ;
239- let updates: String = comments. iter ( ) . map ( |c| format ! ( "\n {}\n " , c. body) ) . collect ( ) ;
240- llm. query ( & prompt, & updates) . await ?
241- } ;
242-
243- let goal = UpdatesGoal {
244- title : title. clone ( ) ,
245- issue_number : issue. number ,
246- issue_assignees : comma ( & issue. assignees ) ,
247- issue_url : IssueId {
248- repository : repository. clone ( ) ,
249- number : issue. number ,
250- }
251- . url ( ) ,
252- is_closed : issue. state == GithubIssueState :: Closed ,
253- updates_markdown : summary,
254- progress : checkboxes ( & issue) ,
255- } ;
256-
257- if comments. len ( ) > 0 {
258- updates. other_goals_with_updates . push ( goal) ;
259- } else {
260- updates. other_goals_without_updates . push ( goal) ;
261- }
262-
263- progress_bar:: inc_progress_bar ( ) ;
264- }
265- Ok ( ( ) )
127+ Ok ( result)
266128}
267129
268130struct Filter < ' f > {
0 commit comments