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' ; 
25
36import  InteractionHistory ,  {  VectorTermsInteractionEvent  }  from  '../interaction-history' ; 
4- import  contentAfter  from  '../lib/content-after' ; 
5- import  parseJSON  from  '../lib/parse-json' ; 
67import  Message  from  '../message' ; 
78import  CompletionService  from  './completion-service' ; 
89
10+ const  debug  =  makeDebug ( 'navie:vector-terms' ) ; 
11+ 
912const  SYSTEM_PROMPT  =  `You are assisting a developer to search a code base. 
1013
1114The 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
2023  be words that will match a feature or domain model object in the code base. They should be the most 
2124  distinctive words in the question. You will prefix the MOST SELECTIVE terms with a '+'. 
2225
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.` ; 
3627
3728const  promptExamples : Message [ ]  =  [ 
3829  { 
3930    content : 'How do I record AppMap data of my Spring app?' , 
4031    role : 'user' , 
4132  } , 
4233  { 
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+     } ) , 
4739    role : 'assistant' , 
4840  } , 
4941
@@ -52,10 +44,11 @@ Terms: record AppMap data Java +Spring`,
5244    role : 'user' , 
5345  } , 
5446  { 
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+     } ) , 
5952    role : 'assistant' , 
6053  } , 
6154
@@ -65,10 +58,11 @@ Terms: user login handle +password validate invalid error`,
6558    role : 'user' , 
6659  } , 
6760  { 
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+     } ) , 
7266    role : 'assistant' , 
7367  } , 
7468
@@ -78,10 +72,11 @@ Terms: +Redis get test-group test-project-1 blob main README`,
7872    role : 'user' , 
7973  } , 
8074  { 
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+     } ) , 
8580    role : 'assistant' , 
8681  } , 
8782
@@ -90,15 +85,20 @@ Terms: test cases +logContext jest`,
9085    role : 'user' , 
9186  } , 
9287  { 
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+     } ) , 
9793    role : 'assistant' , 
9894  } , 
9995] ; 
10096
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+ } ) ; 
102102
103103export  default  class  VectorTermsService  { 
104104  constructor ( 
@@ -119,45 +119,15 @@ export default class VectorTermsService {
119119      } , 
120120    ] ; 
121121
122-     const  response  =  this . completionsService . complete ( messages ,  { 
122+     const  response  =  await   this . completionsService . json ( messages ,   schema ,  { 
123123      model : this . completionsService . miniModelName , 
124124    } ) ; 
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 ; 
162132  } 
163133} 
0 commit comments