Skip to content

Commit 3bb083d

Browse files
committed
added orchestration to call NL2SQL or RAG
1 parent 075f2bb commit 3bb083d

File tree

4 files changed

+231
-162
lines changed

4 files changed

+231
-162
lines changed

db/sql/02-tables.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ create table dbo.semantic_cache
5656
(
5757
[id] int identity primary key nonclustered,
5858
[query] nvarchar(max) not null,
59+
[action] nvarchar(max) not null,
60+
[samples] nvarchar(max) not null,
5961
[embedding] vector(1536) not null,
6062
[query_date] datetime2(0) not null,
6163
[response] nvarchar(max) not null,

db/sql/04-find_samples.sql

Lines changed: 68 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
create or alter procedure [web].[find_samples] @text nvarchar(max), @k int = 50
1+
create or alter procedure [web].[find_samples] @text nvarchar(max), @k int = null
22
as
3-
declare @cached_response nvarchar(max)
4-
declare @retval int, @response nvarchar(max);
3+
declare @response nvarchar(max), @cached_response nvarchar(max);
4+
declare @retval int;
5+
declare @samples nvarchar(max)
56

67
/* Get the embedding for the requested text */
78
declare @qv vector(1536)
@@ -10,163 +11,81 @@ if (@retval != 0) return;
1011

1112
/* Check in the semantic cache to see if a similar question has been already answered */
1213
delete from [dbo].[semantic_cache] where query_date < dateadd(hour, -1, sysdatetime())
14+
1315
select top(1) *, vector_distance('cosine', @qv, embedding) as d
1416
into #c
15-
from [dbo].[semantic_cache] order by d
16-
select @cached_response = response from #c where d < 0.3
17-
18-
if (@cached_response is not null)
19-
begin
20-
set @response = @cached_response
21-
end
22-
else
23-
begin
24-
/* Find the samples most similar to the requested topic */
25-
drop table if exists #s;
26-
select top(@k)
27-
s.id, [name], [description], [url], [notes], [details],
28-
least(
29-
vector_distance('cosine', e.[embedding], @qv),
30-
vector_distance('cosine', ne.[embedding], @qv),
31-
vector_distance('cosine', de.[embedding], @qv)
32-
) as distance_score
33-
into
34-
#s
35-
from
36-
dbo.samples s
37-
inner join
38-
dbo.samples_embeddings e on e.id = s.id
39-
left join
40-
dbo.samples_notes_embeddings ne on e.id = ne.id
41-
left join
42-
dbo.samples_details_embeddings de on e.id = de.id
43-
order by
44-
distance_score asc
45-
--select * from #s
17+
from [dbo].[semantic_cache] order by d;
4618

47-
/* Prepare the JSON string with relevant results to be sent to LLM for evaluation */
48-
declare @s nvarchar(max) = (
49-
select
50-
[id], [name], [description], [notes], [details],
51-
cast((1-distance_score)*100 as int) as similiarity_score
52-
from #s
53-
where distance_score < 0.85
54-
order by distance_score for json path
55-
)
56-
--select @s
19+
select top(1) @cached_response = response from #c where d < 0.3
20+
if (@cached_response is not null) set @response = @cached_response
5721

58-
/* Create the prompt for the LLM */
59-
declare @p nvarchar(max) =
60-
json_object(
61-
'messages': json_array(
62-
json_object(
63-
'role':'system',
64-
'content':'
65-
You as a system assistant who helps users find code samples the user can use to learn the topic they are interested in.
66-
Samples are provided in an assitant message using a JSON Array with the following format: [{id, name, description, note, details, similiarity_score}].
67-
Put in sample_summary output property a markdown short summary of the sample using the provided description, notes, and details.
68-
Use only the provided samples to help you answer the user''s question.
69-
Make sure to use details, notes, and description that are provided in each sample are used only with that sample.
70-
If there are related links or repos in the details of a sample that is included in the answer, include them in the short summary. Include links only if they are related to the sample and if they are available in the note or details of that sample.
71-
If the question cannot be answered by the provided samples, you must say that you don''t know.
72-
If asked question is about topics you don''t know, answer that you don''t know.
73-
'
74-
),
75-
json_object(
76-
'role':'assistant',
77-
'content': 'The available samples are the following:'
78-
),
79-
json_object(
80-
'role':'assistant',
81-
'content': coalesce(@s, 'No samples found for the requested search text.')
82-
),
83-
json_object(
84-
'role':'user',
85-
'content': + @text
86-
)
87-
),
88-
'temperature': 0.2,
89-
'frequency_penalty': 0,
90-
'presence_penalty': 0,
91-
'stop': null
92-
);
22+
/* If no cached response is available then generate a fresh answer */
23+
if (@response is null) begin
24+
25+
/* Orchestrate answer */
26+
declare @rt varchar(50), @rq nvarchar(max)
27+
exec @retval = [web].[orchestrate_request] @text, @rt output, @rq output
28+
if (@retval != 0) return;
9329

94-
declare @js nvarchar(max) = N'{
95-
"type": "json_schema",
96-
"json_schema": {
97-
"name": "samples",
98-
"strict": true,
99-
"schema": {
100-
"type": "object",
101-
"properties": {
102-
"samples": {
103-
"type": "array",
104-
"items": {
105-
"type": "object",
106-
"properties": {
107-
"result_position": {
108-
"type": "number"
109-
},
110-
"id": {
111-
"type": "number"
112-
},
113-
"sample_summary": {
114-
"type": "string"
115-
},
116-
"thoughts": {
117-
"type": "string"
118-
}
119-
},
120-
"required": [
121-
"id",
122-
"sample_summary",
123-
"thoughts",
124-
"result_position"
125-
],
126-
"additionalProperties": false
127-
}
128-
}
129-
},
130-
"required": ["samples"],
131-
"additionalProperties": false
132-
}
133-
}
134-
}'
30+
--print @rt
31+
--print @rq
13532

136-
set @p = json_modify(@p, '$.response_format', json_query(@js))
137-
---select @p
138-
139-
/* Send request to LLM */
140-
begin try
141-
exec @retval = sp_invoke_external_rest_endpoint
142-
@url = '$OPENAI_URL$/openai/deployments/$OPENAI_CHAT_DEPLOYMENT_NAME$/chat/completions?api-version=2024-08-01-preview',
143-
@headers = '{"Content-Type":"application/json"}',
144-
@method = 'POST',
145-
@credential = [$OPENAI_URL$],
146-
@timeout = 120,
147-
@payload = @p,
148-
@response = @response output;
149-
end try
150-
begin catch
151-
select 'REST' as [error], ERROR_NUMBER() as [error_code], ERROR_MESSAGE() as [error_message]
152-
return
153-
end catch
154-
--select @response
33+
/* Find the samples most similar to the requested topic */
34+
if (@rt = 'SEMANTIC') begin
35+
set @k = coalesce(@k, 50)
36+
drop table if exists #s;
37+
select top(@k)
38+
s.id, [name], [description], [url], [notes], [details],
39+
least(
40+
vector_distance('cosine', e.[embedding], @qv),
41+
vector_distance('cosine', ne.[embedding], @qv),
42+
vector_distance('cosine', de.[embedding], @qv)
43+
) as distance_score
44+
into
45+
#s
46+
from
47+
dbo.samples s
48+
inner join
49+
dbo.samples_embeddings e on e.id = s.id
50+
left join
51+
dbo.samples_notes_embeddings ne on e.id = ne.id
52+
left join
53+
dbo.samples_details_embeddings de on e.id = de.id
54+
order by
55+
distance_score asc;
15556

156-
if @retval != 0 begin
157-
select 'OpenAI' as [error], @retval as [error_code], @response as [response]
158-
return
57+
/* Prepare the JSON string with relevant results to be sent to LLM for evaluation */
58+
set @samples = (
59+
select
60+
[id], [name], [description], [notes], [details],
61+
cast((1-distance_score)*100 as int) as similiarity_score
62+
from #s
63+
where distance_score < 0.85
64+
order by distance_score for json path
65+
)
15966
end
16067

161-
declare @refusal nvarchar(max) = (select coalesce(json_value(@response, '$.result.choices[0].refusal'), ''));
68+
/* Find the samples using T-SQL */
69+
if (@rt = 'SQL') begin
70+
declare @trq nvarchar(max) = trim(replace(replace(@rq, char(13), ' '), char(10), ' '));
71+
if (@trq like '%INSERT %' or @trq like '%UPDATE %' or @trq like '%DELETE %' or @trq like '%DROP %' or @trq like '%ALTER %' or @trq like '%CREATE %') begin
72+
--select @trq
73+
select 'NL2SQL' as [error], -1 as [error_code], 'Unauthorized SQL command requested' as [response]
74+
return -1
75+
end
16276

163-
if @refusal != '' begin
164-
select 'OpenAI/Refusal' as [error], @refusal as [refusal], @response as [response]
165-
return
77+
declare @q nvarchar(max) = 'SET @s = (' + @rq + ')';
78+
exec sp_executesql @q, N'@s NVARCHAR(MAX) OUTPUT', @s = @samples output
79+
--print @samples
16680
end
81+
82+
--select @samples;
83+
exec @retval = [web].[generate_answer] @text, @samples, @response output;
84+
if (@retval != 0) return;
16785

168-
insert into dbo.semantic_cache (query, embedding, query_date, response)
169-
values (@text, @qv, sysdatetime(), @response)
86+
/* Cache results */
87+
insert into dbo.semantic_cache (query, [action], samples, embedding, query_date, response)
88+
values (@text, @rt + isnull(':' + @rq, ''), @samples, @qv, sysdatetime(), @response)
17089
end
17190

17291
select
@@ -193,4 +112,5 @@ inner join
193112
dbo.samples as s on s.id = sr.id
194113
order by
195114
sr.result_position
115+
196116
GO

db/sql/10-generate_answer.sql

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
create or alter procedure [web].[generate_answer]
2+
@query_text nvarchar(max),
3+
@source nvarchar(max),
4+
@response nvarchar(max) output
5+
as
6+
declare @retval int;
7+
8+
if (@query_text is null) begin
9+
select 'Generator' as [error], -1 as [error_code], 'Query not provided' as [error_message]
10+
return -1
11+
end
12+
13+
if (@source is null) begin
14+
select 'Generator' as [error], -1 as [error_code], 'Sample list not provided' as [error_message]
15+
return -1
16+
end
17+
18+
19+
/* Create the prompt for the LLM */
20+
declare @p nvarchar(max) =
21+
json_object(
22+
'messages': json_array(
23+
json_object(
24+
'role':'system',
25+
'content':'
26+
You as a system assistant who helps users find code samples the user can use to learn the topic they are interested in.
27+
Samples are provided in an assitant message using a JSON Array with the following format: [{id, name, description, note, details, similiarity_score}].
28+
Put in sample_summary output property a markdown short summary of the sample using the provided description, notes, and details.
29+
Use only the provided samples to help you answer the question.
30+
Use only the informations available in the provided JSON to answer the question.
31+
Make sure to use details, notes, and description that are provided in each sample are used only with that sample.
32+
If there are related links or repos in the details of a sample that is included in the answer, include them in the short summary. Include links only if they are related to the sample and if they are available in the note or details of that sample.
33+
If the question cannot be answered by the provided samples, you must say that you don''t know.
34+
If asked question is about topics you don''t know, answer that you don''t know.
35+
If no samples are provided, say that you canno''t answer as no samples have been found.
36+
'
37+
),
38+
json_object(
39+
'role':'assistant',
40+
'content': 'The available samples are the following:'
41+
),
42+
json_object(
43+
'role':'assistant',
44+
'content': coalesce(@source, '')
45+
),
46+
json_object(
47+
'role':'user',
48+
'content': + @query_text
49+
)
50+
),
51+
'temperature': 0.2,
52+
'frequency_penalty': 0,
53+
'presence_penalty': 0,
54+
'stop': null
55+
);
56+
57+
declare @js nvarchar(max) = N'{
58+
"type": "json_schema",
59+
"json_schema": {
60+
"name": "samples",
61+
"strict": true,
62+
"schema": {
63+
"type": "object",
64+
"properties": {
65+
"samples": {
66+
"type": "array",
67+
"items": {
68+
"type": "object",
69+
"properties": {
70+
"result_position": {
71+
"type": "number"
72+
},
73+
"id": {
74+
"type": "number"
75+
},
76+
"sample_summary": {
77+
"type": "string"
78+
},
79+
"thoughts": {
80+
"type": "string"
81+
}
82+
},
83+
"required": [
84+
"id",
85+
"sample_summary",
86+
"thoughts",
87+
"result_position"
88+
],
89+
"additionalProperties": false
90+
}
91+
}
92+
},
93+
"required": ["samples"],
94+
"additionalProperties": false
95+
}
96+
}
97+
}'
98+
99+
set @p = json_modify(@p, '$.response_format', json_query(@js))
100+
---select @p
101+
102+
/* Send request to LLM */
103+
begin try
104+
exec @retval = sp_invoke_external_rest_endpoint
105+
@url = '$OPENAI_URL$/openai/deployments/$OPENAI_CHAT_DEPLOYMENT_NAME$/chat/completions?api-version=2024-08-01-preview',
106+
@headers = '{"Content-Type":"application/json"}',
107+
@method = 'POST',
108+
@credential = [$OPENAI_URL$],
109+
@timeout = 120,
110+
@payload = @p,
111+
@response = @response output;
112+
end try
113+
begin catch
114+
select 'Generator:REST' as [error], ERROR_NUMBER() as [error_code], ERROR_MESSAGE() as [error_message]
115+
return -1
116+
end catch
117+
--select @response
118+
119+
if @retval != 0 begin
120+
select 'Generator:OpenAI' as [error], @retval as [error_code], @response as [response]
121+
return @retval
122+
end
123+
124+
declare @refusal nvarchar(max) = (select coalesce(json_value(@response, '$.result.choices[0].refusal'), ''));
125+
126+
if @refusal != '' begin
127+
select 'Generator:OpenAI/Refusal' as [error], @refusal as [refusal], @response as [response]
128+
return -1
129+
end
130+
GO

0 commit comments

Comments
 (0)