1use secrecy::ExposeSecret;
2use serde_json::json;
3
4use crate::{
5 llm_utils::{azure_search_configuration, azure_search_request},
6 prelude::*,
7};
8
9const API_VERSION: &str = "2024-07-01";
10
11pub async fn does_skillset_exist(
12 skillset_name: &str,
13 app_config: &ApplicationConfiguration,
14) -> ChatbotResult<bool> {
15 let search_config = azure_search_configuration(app_config)?;
16
17 let mut url = search_config.search_endpoint.clone();
18 url.set_path(&format!("skillsets('{}')", skillset_name));
19 url.set_query(Some(&format!("api-version={}", API_VERSION)));
20
21 let response = azure_search_request(reqwest::Method::GET, url, search_config)
22 .send()
23 .await?;
24
25 if response.status().is_success() {
26 Ok(true)
27 } else if response.status() == 404 {
28 Ok(false)
29 } else {
30 let status = response.status();
31 let error_text = response.text().await?;
32 Err(chatbot_err!(
33 FailedAzureResponse,
34 format!(
35 "Error checking if skillset exists. Status: {}. Error: {}",
36 status, error_text
37 )
38 ))
39 }
40}
41
42pub async fn create_skillset(
43 skillset_name: &str,
44 target_index_name: &str,
45 app_config: &ApplicationConfiguration,
46) -> ChatbotResult<()> {
47 let search_config = azure_search_configuration(app_config)?;
48
49 let mut url = search_config.search_endpoint.clone();
50 url.set_path(&format!("skillsets/{}", skillset_name));
51 url.set_query(Some(&format!("api-version={}", API_VERSION)));
52
53 let skillset_definition = json!({
54 "name": skillset_name,
55 "description": "Skillset to chunk documents and generate embeddings",
56 "skills": [
57 {
58 "@odata.type": "#Microsoft.Skills.Text.SplitSkill",
59 "name": "#1",
60 "description": "Split skill to chunk documents",
61 "context": "/document",
62 "defaultLanguageCode": "en",
63 "textSplitMode": "pages",
64 "maximumPageLength": 2000,
65 "pageOverlapLength": 500,
66 "maximumPagesToTake": 0,
67 "inputs": [
68 {
69 "name": "text",
70 "source": "/document/content"
71 },
72 {
73 "name": "languageCode",
74 "source": "/document/language"
75 }
76 ],
77 "outputs": [
78 {
79 "name": "textItems",
80 "targetName": "pages"
81 }
82 ]
83 },
84 {
85 "@odata.type": "#Microsoft.Skills.Text.AzureOpenAIEmbeddingSkill",
86 "name": "#2",
87 "description": null,
88 "context": "/document/pages/*",
89 "resourceUri": search_config.vectorizer_resource_uri.clone(),
90 "apiKey": search_config.vectorizer_api_key.expose_secret(),
91 "deploymentId": search_config.vectorizer_deployment_id.clone(),
92 "dimensions": 1536,
93 "modelName": search_config.vectorizer_model_name.clone(),
94 "inputs": [
95 {
96 "name": "text",
97 "source": "/document/pages/*",
98 "sourceContext": null,
99 "inputs": []
100 }
101 ],
102 "outputs": [
103 {
104 "name": "embedding",
105 "targetName": "text_vector"
106 }
107 ],
108 "authIdentity": null
109 }
110 ],
111 "cognitiveServices": null,
112 "knowledgeStore": null,
113 "indexProjections": {
114 "selectors": [
115 {
116 "targetIndexName": target_index_name,
117 "parentKeyFieldName": "parent_id",
118 "sourceContext": "/document/pages/*",
119 "mappings": [
120 {
121 "name": "text_vector",
122 "source": "/document/pages/*/text_vector",
123 "sourceContext": null,
124 "inputs": []
125 },
126 {
127 "name": "chunk",
128 "source": "/document/pages/*",
129 "sourceContext": null,
130 "inputs": []
131 },
132 {
133 "name": "title",
134 "source": "/document/title",
135 "sourceContext": null,
136 "inputs": []
137 },
138 {
139 "name": "url",
140 "source": "/document/url",
141 "sourceContext": null,
142 "inputs": []
143 },
144 {
145 "name": "course_id",
146 "source": "/document/course_id",
147 "sourceContext": null,
148 "inputs": []
149 },
150 {
151 "name": "language",
152 "source": "/document/language",
153 "sourceContext": null,
154 "inputs": []
155 },
156 {
157 "name": "filepath",
158 "source": "/document/filepath",
159 "sourceContext": null,
160 "inputs": []
161 },
162 {
163 "name": "chunk_context",
164 "source": "/document/chunk_context",
165 "sourceContext": null,
166 "inputs": []
167 },
168 ]
169 }
170 ],
171 "parameters": {
172 "projectionMode": "skipIndexingParentDocuments"
173 }
174 },
175 "encryptionKey": null
176 });
177
178 let response = azure_search_request(reqwest::Method::PUT, url, search_config)
179 .json(&skillset_definition)
180 .send()
181 .await?;
182
183 if response.status().is_success() {
184 Ok(())
185 } else {
186 let status = response.status();
187 let error_text = response.text().await?;
188 Err(chatbot_err!(
189 FailedAzureResponse,
190 format!(
191 "Error creating skillset. Status: {}. Error: {}",
192 status, error_text
193 )
194 ))
195 }
196}