-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathai.py
More file actions
110 lines (95 loc) · 4.19 KB
/
ai.py
File metadata and controls
110 lines (95 loc) · 4.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import controlflow as cf
from typing import Optional, Union
from pydantic import BaseModel
from .github import PullRequest, Issue, Label
@cf.flow
def labeling_workflow(
item: Union[PullRequest, Issue],
labels: list[Label],
instructions: Optional[str] = None,
context_files: Optional[dict[str, str]] = None,
llm_model: Optional[str] = None,
) -> list[str]:
def validate_labels(result: list[str]):
if any(label not in [l.name for l in labels] for label in result):
raise ValueError(
f"Invalid labels. Must be one of {', '.join(f"{l.name}" for l in labels)}"
)
return result
# Create an agent specialized in GitHub labeling
labeler = cf.Agent(
name="GitHub Labeler",
instructions="""
You are an expert AI that automatically labelling GitHub issues and pull
requests. You always pay close attention to label instructions.
""",
model=llm_model or "openai/gpt-4o-mini",
)
class Reasoning(BaseModel):
label_name: str
should_apply: bool
# Format linked items for context
linked_items_context = ""
if item.linked_items:
linked_items_context = "\nLinked Items:\n"
for linked in item.linked_items:
linked_items_context += f"""
{linked.type.replace('_', ' ').title()} #{linked.number}:
Title: {linked.title}
Labels: {', '.join(linked.labels)}
Body: {linked.body}
"""
reasoning = cf.run(
"""
Consider the provided PR/issue, its context, and any provided
instructions. Examine each available label carefully. Your job is to
choose which labels to apply to the PR/issue.
Each label has a name, optional description, and optional instructions.
Treat all three as inputs for understanding whether the label is
relevant. Evaluate each label independently.
For labels that may be appropriate, provide a complete rationale of
whether you would assign them to the PR/issue, taking your instructions
and the label's instructions into account. Some labels will have
specific instructions about when to apply them, or whether to apply them
at all. Be sure to reference all relevant context and instructions in
your reasoning.
You do not need to return reasoning about labels that are obviously
irrelevant.
When evaluating labels, consider any linked items and their context:
- Look for patterns or relationships between the current item and linked items
- Consider if linked items provide additional context about the scope or impact
- Check if linked items have relevant labels that could inform this decision
""",
instructions=instructions,
result_type=list[Reasoning],
context={
"pr_or_issue_to_label": item,
"available_labels": dict(enumerate(labels)),
"additional_context": context_files,
"labeling_instructions": instructions,
"linked_items_context": linked_items_context,
},
agents=[labeler],
completion_tools=["SUCCEED"], # the task can not be marked as failed
model_kwargs=dict(tool_choice="required"), # prevent chatting
)
decision = [r.label_name for r in reasoning if r.should_apply]
# --- old two-step approach. Adding `should_apply` to the reasoning model
# appears to match performance in a single step.
#
# decision = cf.run(
# """
# Based on the reasoning for each label, return the list of labels that
# should be applied. If no labels apply, return an empty list.
# """,
# result_type=list[str],
# result_validator=validate_labels,
# context={"reasoning": reasoning, "available_labels": labels},
# agents=[labeler],
# completion_tools=["SUCCEED"], # the task can not be marked as failed
# model_kwargs=dict(tool_choice="required"), # prevent chatting
# )
print(f"Available labels: {dict(enumerate(labels))}")
print(f"\n\nReasoning: {reasoning}")
print(f"\n\nApplied labels: {decision}")
return decision