Packages
fnord
0.8.21
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/ai/agent/coder/range_finder.ex
defmodule AI.Agent.Coder.RangeFinder do
@max_attempts 3
@model AI.Model.reasoning(:medium)
@prompt """
You are an AI coding assistant within a larger AI system.
The Coordinating Agent asks you to identify a contiguous range of lines in a file based on the provided instructions for a coding task it is planning.
Identify the *single*, contiguous range of lines in the file to be replaced, based on the instructions.
Use your programming expertise to ensure that the range is appropriate to the intent of the instructions.
For example, if asked to add a function at the end of the file, ensure the range would place the new function in a syntactically appropriate location.
If the instructions are ambiguous or do not clearly point to a single range, respond with an error message that clearly explains the problem.
Note: The file contents include 1-based line numbers at the start of each line, separated by a pipe (|).
# Response Format
Constraints:
- `start_line` <= `end_line`
- `start_line` >= 1
- `end_line` <= the number of lines in the file
- if `start_line` == `end_line`, the range is a single line
- The instructions will be applied to *fully* replace the identified range, so the range must cover the entire area to be replaced.
You MUST respond ONLY with a single JSON object with the following fields, based on whether you were able to identify a range:
**Success:** `{"start_line": <start_line>, "end_line": <end_line>}`
**Failure:** `{"error": "<error message>"}`
Do not include any other text, comments, explanations, or markdown fences in your response.
"""
# ----------------------------------------------------------------------------
# Behaviour Implementation
# ----------------------------------------------------------------------------
@behaviour AI.Agent
@impl AI.Agent
def get_response(opts) do
with {:ok, instructions} <- Map.fetch(opts, :instructions),
{:ok, file} <- Map.fetch(opts, :file) do
identify_range(file, instructions)
else
:error -> {:error, "Incorrect arguments passed to AI.Agent.Coder.RangeFinder."}
end
end
# ----------------------------------------------------------------------------
# Internals
# ----------------------------------------------------------------------------
@spec identify_range(binary, binary, integer) ::
{:ok, {integer, integer}}
| {:identify_error, binary}
| {:error, binary}
| {:error, :max_attempts_reached}
defp identify_range(file, instructions, attempt \\ 1)
defp identify_range(_, _, attempt) when attempt > @max_attempts do
{:error, "Maximum attempts reached while trying to apply changes."}
end
defp identify_range(file, instructions, attempt) do
file_contents =
file
|> File.read!()
|> Util.numbered_lines()
user = """
Please identify the range of lines in the file that should be replaced based on the following instructions.
# Instructions
#{instructions}
# File: #{file}
```
#{file_contents}
```
"""
AI.Completion.get(
model: @model,
messages: [
AI.Util.system_msg(@prompt),
AI.Util.user_msg(user)
]
)
|> case do
{:ok, %{response: response}} ->
response
|> Jason.decode()
|> case do
{:ok, %{"start_line" => start_line, "end_line" => end_line}} ->
file_contents
|> validate_range(start_line, end_line)
|> case do
:ok -> {:ok, {start_line, end_line}}
{:error, _error} -> identify_range(file, instructions, attempt + 1)
end
{:ok, %{"error" => msg}} ->
{:identify_error, msg}
end
{:error, _reason} ->
identify_range(file, instructions, attempt + 1)
end
end
@spec validate_range(binary, integer, integer) :: :ok | {:error, binary}
defp validate_range(contents, start_line, end_line) do
lines = num_lines(contents)
cond do
start_line < 1 ->
{:error, "Start line must be greater than or equal to 1."}
start_line > lines ->
{:error, "Start line exceeds the number of lines in the file."}
end_line < start_line ->
{:error, "End line must be greater than or equal to start line."}
lines > 0 && end_line > lines ->
{:error, "End line exceeds the number of lines in the file."}
true ->
:ok
end
end
@spec num_lines(binary) :: integer
defp num_lines(str) do
str
|> String.split("\n", trim: false)
|> length()
end
end