@@ -9,6 +9,77 @@ local job_runner = nil
99
1010--- @alias QueryToolArgs { project_root : string , count : integer , query : string[] }
1111
12+ --- @alias chat_id integer
13+ --- @alias result_id string
14+ --- @type <chat_id : result_id>
15+ local result_tracker = {}
16+
17+ --- @param results VectorCode.QueryResult[]
18+ --- @param chat CodeCompanion.Chat
19+ --- @return VectorCode.QueryResult[]
20+ local filter_results = function (results , chat )
21+ local existing_refs = chat .refs or {}
22+
23+ existing_refs = vim
24+ .iter (existing_refs )
25+ :filter (
26+ --- @param ref CodeCompanion.Chat.Ref
27+ function (ref )
28+ return ref .source == cc_common .tool_result_source or ref .path or ref .bufnr
29+ end
30+ )
31+ :map (
32+ --- @param ref CodeCompanion.Chat.Ref
33+ function (ref )
34+ if ref .source == cc_common .tool_result_source then
35+ return ref .id
36+ elseif ref .path then
37+ return ref .path
38+ elseif ref .bufnr then
39+ return vim .api .nvim_buf_get_name (ref .bufnr )
40+ end
41+ end
42+ )
43+ :totable ()
44+
45+ --- @type VectorCode.QueryResult[]
46+ local filtered_results = vim
47+ .iter (results )
48+ :filter (
49+ --- @param res VectorCode.QueryResult
50+ function (res )
51+ -- return true if res should be kept
52+ if res .chunk then
53+ if res .chunk_id == nil then
54+ -- no chunk_id, always include
55+ return true
56+ end
57+ if
58+ result_tracker [chat .id ] ~= nil and result_tracker [chat .id ][res .chunk_id ]
59+ then
60+ return false
61+ end
62+ return not vim .tbl_contains (existing_refs , res .chunk_id )
63+ else
64+ if result_tracker [chat .id ] ~= nil and result_tracker [chat .id ][res .path ] then
65+ return false
66+ end
67+ return not vim .tbl_contains (existing_refs , res .path )
68+ end
69+ end
70+ )
71+ :totable ()
72+
73+ for _ , res in pairs (filtered_results ) do
74+ if result_tracker [chat .id ] == nil then
75+ result_tracker [chat .id ] = {}
76+ end
77+ result_tracker [chat .id ][res .chunk_id or res .path ] = true
78+ end
79+
80+ return filtered_results
81+ end
82+
1283--- @param opts VectorCode.CodeCompanion.QueryToolOpts ?
1384--- @return CodeCompanion.Agent.Tool
1485return check_cli_wrap (function (opts )
@@ -148,6 +219,7 @@ You may include multiple keywords in the command.
148219 description = [[
149220Query messages used for the search. They should also contain relevant keywords.
150221For example, you should include `parameter`, `arguments` and `return value` for the query `function`.
222+ If a query returned empty or repeated results, you should avoid using these query keywords, unless the user instructed otherwise.
151223 ]] ,
152224 },
153225 count = {
@@ -219,6 +291,9 @@ For example, you should include `parameter`, `arguments` and `return value` for
219291 if opts .max_num > 0 then
220292 max_result = math.min (opts .max_num or 1 , max_result )
221293 end
294+ if opts .no_duplicate then
295+ stdout = filter_results (stdout , agent .chat )
296+ end
222297 for i , file in pairs (stdout ) do
223298 if i <= max_result then
224299 if i == 1 then
@@ -240,14 +315,14 @@ For example, you should include `parameter`, `arguments` and `return value` for
240315 user_message
241316 )
242317 if not opts .chunk_mode then
243- -- skip referencing because there will be multiple chunks with the same path (id).
244- -- TODO: figure out a way to deduplicate.
245- agent .chat .references :add ({
318+ -- only add to reference if running in full document mode
319+ local ref = {
246320 source = cc_common .tool_result_source ,
247321 id = file .path ,
248322 path = file .path ,
249323 opts = { visible = false },
250- })
324+ }
325+ agent .chat .references :add (ref )
251326 end
252327 end
253328 end
0 commit comments