Skip to content

Commit 44de9c5

Browse files
committed
feat: wire up Azure AI Search, fix seed script import bug
1 parent 633ff03 commit 44de9c5

1 file changed

Lines changed: 82 additions & 42 deletions

File tree

scripts/seed_search_index.py

Lines changed: 82 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,70 +17,110 @@
1717
load_dotenv()
1818

1919
from foundry_iq import KNOWLEDGE_BASE
20+
import requests
2021

2122

22-
def create_index_if_not_exists(search_client):
23-
"""Create the search index with the expected schema."""
24-
from azure.search.documents.indexes import SearchIndexClient
25-
from azure.search.documents.indexes.models import (
26-
SearchIndex, SimpleField, SearchableField, SearchFieldDataType,
27-
)
23+
def create_index():
24+
"""Create the search index via REST API."""
25+
endpoint = os.getenv('FOUNDRY_IQ_ENDPOINT').rstrip('/')
2826
index_name = os.getenv('FOUNDRY_IQ_INDEX_NAME', 'sleepiq-medical-knowledge')
29-
index_client = SearchIndexClient(
30-
endpoint=os.getenv('FOUNDRY_IQ_ENDPOINT'),
31-
credential=AzureKeyCredential(os.getenv('FOUNDRY_IQ_API_KEY')),
32-
)
33-
try:
34-
index_client.get_index(index_name)
35-
print(f'Index "{index_name}" already exists.')
36-
except:
37-
fields = [
38-
SimpleField(name='id', type=SearchFieldDataType.String, key=True),
39-
SearchableField(name='source', type=SearchFieldDataType.String, filterable=True),
40-
SearchableField(name='content', type=SearchFieldDataType.String),
41-
SearchableField(name='keywords', type=SearchFieldDataType.Collection(SearchFieldDataType.String),
42-
filterable=True, searchable=True),
27+
api_key = os.getenv('FOUNDRY_IQ_API_KEY')
28+
29+
url = f"{endpoint}/indexes/{index_name}?api-version=2023-11-01"
30+
headers = {
31+
"Content-Type": "application/json",
32+
"api-key": api_key,
33+
}
34+
body = {
35+
"name": index_name,
36+
"fields": [
37+
{"name": "id", "type": "Edm.String", "key": True},
38+
{"name": "source", "type": "Edm.String", "filterable": True, "searchable": True},
39+
{"name": "content", "type": "Edm.String", "searchable": True},
40+
{"name": "keywords", "type": "Collection(Edm.String)", "filterable": True, "searchable": True},
4341
]
44-
index = SearchIndex(name=index_name, fields=fields)
45-
index_client.create_index(index)
42+
}
43+
44+
# Check if index exists
45+
resp = requests.get(url, headers=headers)
46+
if resp.status_code == 200:
47+
print(f'Index "{index_name}" already exists.')
48+
return
49+
50+
# Create it
51+
resp = requests.put(url, headers=headers, json=body)
52+
if resp.status_code in (201, 204):
4653
print(f'Created index "{index_name}".')
54+
else:
55+
print(f'Failed to create index: {resp.status_code} {resp.text}')
56+
sys.exit(1)
4757

4858

4959
def seed():
50-
endpoint = os.getenv('FOUNDRY_IQ_ENDPOINT')
60+
endpoint = os.getenv('FOUNDRY_IQ_ENDPOINT', '').rstrip('/')
5161
index_name = os.getenv('FOUNDRY_IQ_INDEX_NAME', 'sleepiq-medical-knowledge')
5262
api_key = os.getenv('FOUNDRY_IQ_API_KEY')
5363

5464
if not endpoint or not api_key:
5565
print('ERROR: Set FOUNDRY_IQ_ENDPOINT and FOUNDRY_IQ_API_KEY in .env')
5666
sys.exit(1)
5767

58-
from azure.core.credentials import AzureKeyCredential
59-
from azure.search.documents import SearchClient
60-
61-
client = SearchClient(endpoint=endpoint, index_name=index_name,
62-
credential=AzureKeyCredential(api_key))
68+
create_index()
6369

64-
create_index_if_not_exists(client)
70+
# Upload documents via REST API
71+
url = f"{endpoint}/indexes/{index_name}/docs/index?api-version=2023-11-01"
72+
headers = {
73+
"Content-Type": "application/json",
74+
"api-key": api_key,
75+
}
6576

6677
documents = []
6778
for entry in KNOWLEDGE_BASE:
6879
documents.append({
69-
'id': entry['id'],
70-
'source': entry['source'],
71-
'content': entry['content'],
72-
'keywords': entry['keywords'],
80+
"@search.action": "upload",
81+
"id": entry["id"],
82+
"source": entry["source"],
83+
"content": entry["content"],
84+
"keywords": entry["keywords"],
7385
})
7486

75-
result = client.upload_documents(documents)
76-
succeeded = sum(1 for r in result if r.succeeded)
77-
failed = len(result) - succeeded
78-
print(f'Uploaded {succeeded}/{len(result)} knowledge entries to "{index_name}".')
79-
if failed:
80-
print(f'Failed: {failed}')
81-
for r in result:
82-
if not r.succeeded:
83-
print(f' - {r.key}: {r.error}')
87+
batch_size = 10
88+
total_succeeded = 0
89+
total_failed = 0
90+
91+
for i in range(0, len(documents), batch_size):
92+
batch = documents[i:i + batch_size]
93+
body = {"value": batch}
94+
resp = requests.post(url, headers=headers, json=body)
95+
result = resp.json()
96+
97+
if resp.status_code == 200 or resp.status_code == 201:
98+
for item in result.get("value", []):
99+
if item.get("status", False):
100+
total_succeeded += 1
101+
else:
102+
total_failed += 1
103+
print(f' Failed: {item.get("key")}: {item.get("errorMessage")}')
104+
else:
105+
print(f'Upload batch failed: {resp.status_code} {resp.text}')
106+
total_failed += len(batch)
107+
108+
print(f'Uploaded {total_succeeded}/{len(documents)} knowledge entries to "{index_name}".')
109+
if total_failed:
110+
print(f'Failed: {total_failed}')
111+
112+
# Verify by search
113+
print("\nVerifying with test search...")
114+
search_url = f"{endpoint}/indexes/{index_name}/docs/search?api-version=2023-11-01"
115+
test_resp = requests.post(search_url, headers=headers, json={"search": "AHI classification", "top": 2})
116+
if test_resp.status_code == 200:
117+
results = test_resp.json()
118+
count = results.get("@odata.count", len(results.get("value", [])))
119+
print(f'Search works! Found {count} results for "AHI classification".')
120+
for r in results.get("value", []):
121+
print(f' - [{r["id"]}] {r["source"]}')
122+
else:
123+
print(f'Search test failed: {test_resp.status_code} {test_resp.text}')
84124

85125

86126
if __name__ == '__main__':

0 commit comments

Comments
 (0)