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