-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathai.ex
147 lines (124 loc) · 3.64 KB
/
ai.ex
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
defmodule Algora.AI do
require Logger
alias Algora.{Github, Workspace, Util, Repo}
@batch_size 20
@max_concurrency 5
@doc """
Recommend a bounty for an issue
## Example:
Algora.AI.recommend_bounty("https://github.com/calcom/cal.com/issues/6315")
"""
def recommend_bounty(url) do
with {:ok, issue} <- get_issue(url),
{:ok, top_references, similar_issues} <- find_similar_issues(issue),
{:ok, comments} <- list_comments(url),
{:ok, amount} <- get_bounty_recommendation(issue, comments, similar_issues) do
{:ok, amount, top_references}
end
end
@doc """
Add training data
## Example:
Algora.AI.add_training_data(["calcom/cal.com#6315", "remotion-dev/remotion#1525"])
"""
def add_training_data(paths) do
with {:ok, issues} <- fetch_issues(paths),
{:ok, comments} <- fetch_comments(paths) do
for issue <- issues do
issue
|> Map.put(:body, maybe_to_na(issue.body))
|> Map.put(:comments, comments |> Enum.filter(fn c -> c.path == issue.path end))
|> Workspace.create_issue!()
end
end
end
defp maybe_to_na(nil), do: "N/A"
defp maybe_to_na(""), do: "N/A"
defp maybe_to_na(value), do: value
defp fetch_comments(paths) do
Util.with_cache(
&Github.Archive.list_comments/1,
paths,
cache_dir: ".local/comments",
max_concurrency: @max_concurrency,
batch_size: @batch_size
)
end
defp fetch_issues(paths) do
Util.with_cache(
&Github.Archive.list_issues/1,
paths,
cache_dir: ".local/issues",
max_concurrency: @max_concurrency,
batch_size: @batch_size
)
end
def get_issue(url) do
case Github.Client.get_issue_from_url(url) do
{:ok, %{"title" => title, "body" => body}} -> {:ok, %{title: title, body: body}}
error -> error
end
end
def list_comments(url) do
try do
{:ok, list_comments!(url)}
rescue
_ -> {:error, :failed_to_fetch_comments}
end
end
defp list_comments!(url) do
{:ok, comments} = Github.Client.list_comments_from_url(url)
comments
|> Enum.map(fn %{"body" => body, "user" => %{"login" => login}} ->
%{body: body, user: %{login: login}}
end)
end
def find_similar_issues(issue) do
similar_issues = Workspace.search_issues_like(issue, limit: 10)
top_references =
similar_issues
|> Enum.map(fn issue ->
%{
path: issue.path,
title: issue.title,
bounty: issue.bounty
}
end)
{:ok, top_references, similar_issues}
end
def get_bounty_recommendation(issue, comments, similar_issues) do
issues_text =
similar_issues
|> Enum.map_join("\n", fn similar_issue ->
"""
Title: #{similar_issue.title}
Bounty: #{similar_issue.bounty}
"""
end)
prompt = """
Based on the following issue and similar issues with their bounties, recommend an appropriate bounty amount.
Consider the complexity implied by both the title and description, as well as patterns in existing bounties.
Provide only a numeric response in USD (no symbols or text).
Current Issue:
Title: #{issue.title}
Description: #{issue.body}
Comments: #{Jason.encode!(comments)}
Similar issues:
#{issues_text}
"""
{:ok, %{rows: [[recommendation]]}} =
Repo.query(
"""
SELECT vectorize.generate(
input => $1,
model => 'openai/gpt-4o'
)
""",
[prompt]
)
case Decimal.parse(String.trim(recommendation)) do
{amount, _} -> {:ok, amount}
_ -> {:error, :invalid_recommendation}
end
end
end