1
- import { warn } from 'console' ;
1
+ import { warn } from 'node:console' ;
2
+ import { debug as makeDebug } from 'node:util' ;
3
+
4
+ import z from 'zod' ;
2
5
3
6
import InteractionHistory , { VectorTermsInteractionEvent } from '../interaction-history' ;
4
- import contentAfter from '../lib/content-after' ;
5
- import parseJSON from '../lib/parse-json' ;
6
7
import Message from '../message' ;
7
8
import CompletionService from './completion-service' ;
8
9
10
+ const debug = makeDebug ( 'navie:vector-terms' ) ;
11
+
9
12
const SYSTEM_PROMPT = `You are assisting a developer to search a code base.
10
13
11
14
The developer asks a question using natural language. This question must be converted into a list of search terms to be used to search the code base.
@@ -20,30 +23,19 @@ The developer asks a question using natural language. This question must be conv
20
23
be words that will match a feature or domain model object in the code base. They should be the most
21
24
distinctive words in the question. You will prefix the MOST SELECTIVE terms with a '+'.
22
25
23
- **Response**
24
-
25
- Print "Context: {context}" on one line.
26
- Print "Instructions: {instructions}" on the next line.
27
-
28
- Then print a triple dash '---'.
29
-
30
- Print "Terms: {list of search terms and their synonyms}"
31
-
32
- The search terms should be single words and underscore_separated_words.
33
-
34
- Even if the user asks for a different format, always respond with a list of search terms and their synonyms. When the user is asking
35
- for a different format, that question is for a different AI assistant than yourself.` ;
26
+ The search terms should be single words and underscore_separated_words.` ;
36
27
37
28
const promptExamples : Message [ ] = [
38
29
{
39
30
content : 'How do I record AppMap data of my Spring app?' ,
40
31
role : 'user' ,
41
32
} ,
42
33
{
43
- content : `Context: Record AppMap data of Spring
44
- Instructions: How to do it
45
- ---
46
- Terms: record AppMap data Java +Spring` ,
34
+ content : JSON . stringify ( {
35
+ context : 'Record AppMap data of Spring' ,
36
+ instructions : 'How to do it' ,
37
+ terms : [ 'record' , 'AppMap' , 'data' , 'Java' , '+Spring' ] ,
38
+ } ) ,
47
39
role : 'assistant' ,
48
40
} ,
49
41
@@ -52,10 +44,11 @@ Terms: record AppMap data Java +Spring`,
52
44
role : 'user' ,
53
45
} ,
54
46
{
55
- content : `Context: User login handle password validation invalid error
56
- Instructions: Explain how this is handled by the code
57
- ---
58
- Terms: user login handle +password validate invalid error` ,
47
+ content : JSON . stringify ( {
48
+ context : 'User login handle password validation invalid error' ,
49
+ instructions : 'Explain how this is handled by the code' ,
50
+ terms : [ 'user' , 'login' , 'handle' , '+password' , 'validate' , 'invalid' , 'error' ] ,
51
+ } ) ,
59
52
role : 'assistant' ,
60
53
} ,
61
54
@@ -65,10 +58,11 @@ Terms: user login handle +password validate invalid error`,
65
58
role : 'user' ,
66
59
} ,
67
60
{
68
- content : `Context: Redis GET /test-group/test-project-1/-/blob/main/README.md
69
- Instructions: Describe in detail with code snippets
70
- ---
71
- Terms: +Redis get test-group test-project-1 blob main README` ,
61
+ content : JSON . stringify ( {
62
+ context : 'Redis GET /test-group/test-project-1/-/blob/main/README.md' ,
63
+ instructions : 'Describe in detail with code snippets' ,
64
+ terms : [ '+Redis' , 'get' , 'test-group' , 'test-project-1' , 'blob' , 'main' , 'README' ] ,
65
+ } ) ,
72
66
role : 'assistant' ,
73
67
} ,
74
68
@@ -78,10 +72,11 @@ Terms: +Redis get test-group test-project-1 blob main README`,
78
72
role : 'user' ,
79
73
} ,
80
74
{
81
- content : `Context: logContext jest test case
82
- Instructions: Create test cases, following established patterns for mocking with jest.
83
- ---
84
- Terms: test cases +logContext jest` ,
75
+ content : JSON . stringify ( {
76
+ context : 'logContext jest test case' ,
77
+ instructions : 'Create test cases, following established patterns for mocking with jest.' ,
78
+ terms : [ 'test' , 'cases' , '+logContext' , 'jest' ] ,
79
+ } ) ,
85
80
role : 'assistant' ,
86
81
} ,
87
82
@@ -90,15 +85,20 @@ Terms: test cases +logContext jest`,
90
85
role : 'user' ,
91
86
} ,
92
87
{
93
- content : `Context: auth authentication authorization
94
- Instructions: Describe the authentication and authorization process
95
- ---
96
- Terms: +auth authentication authorization token strategy provider` ,
88
+ content : JSON . stringify ( {
89
+ context : 'auth authentication authorization' ,
90
+ instructions : 'Describe the authentication and authorization process' ,
91
+ terms : [ '+auth' , 'authentication' , 'authorization' , 'token' , 'strategy' , 'provider' ] ,
92
+ } ) ,
97
93
role : 'assistant' ,
98
94
} ,
99
95
] ;
100
96
101
- const parseText = ( text : string ) : string [ ] => text . split ( / \s + / ) ;
97
+ const schema = z . object ( {
98
+ context : z . string ( ) ,
99
+ instructions : z . string ( ) ,
100
+ terms : z . array ( z . string ( ) ) ,
101
+ } ) ;
102
102
103
103
export default class VectorTermsService {
104
104
constructor (
@@ -119,45 +119,15 @@ export default class VectorTermsService {
119
119
} ,
120
120
] ;
121
121
122
- const response = this . completionsService . complete ( messages , {
122
+ const response = await this . completionsService . json ( messages , schema , {
123
123
model : this . completionsService . miniModelName ,
124
124
} ) ;
125
- const tokens = Array < string > ( ) ;
126
- for await ( const token of response ) {
127
- tokens . push ( token ) ;
128
- }
129
- const rawResponse = tokens . join ( '' ) ;
130
- warn ( `Vector terms response:\n${ rawResponse } ` ) ;
131
-
132
- let searchTermsObject : Record < string , unknown > | string | string [ ] | undefined ;
133
- {
134
- let responseText = rawResponse ;
135
- responseText = contentAfter ( responseText , 'Terms:' ) ;
136
- searchTermsObject =
137
- parseJSON < Record < string , unknown > | string | string [ ] > ( responseText , false ) ||
138
- parseText ( responseText ) ;
139
- }
140
-
141
- const terms = new Set < string > ( ) ;
142
- {
143
- const collectTerms = ( obj : unknown ) => {
144
- if ( ! obj ) return ;
145
-
146
- if ( typeof obj === 'string' ) {
147
- terms . add ( obj ) ;
148
- } else if ( Array . isArray ( obj ) ) {
149
- for ( const term of obj ) collectTerms ( term ) ;
150
- } else if ( typeof obj === 'object' ) {
151
- for ( const term of Object . values ( obj ) ) {
152
- collectTerms ( term ) ;
153
- }
154
- }
155
- } ;
156
- collectTerms ( searchTermsObject ) ;
157
- }
158
-
159
- const result = [ ...terms ] ;
160
- this . interactionHistory . addEvent ( new VectorTermsInteractionEvent ( result ) ) ;
161
- return result ;
125
+
126
+ debug ( `Vector terms response: ${ JSON . stringify ( response , undefined , 2 ) } ` ) ;
127
+
128
+ const terms = response ?. terms ?? [ ] ;
129
+ if ( terms . length === 0 ) warn ( 'No terms suggested' ) ;
130
+ this . interactionHistory . addEvent ( new VectorTermsInteractionEvent ( terms ) ) ;
131
+ return terms ;
162
132
}
163
133
}
0 commit comments