Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 70 additions & 30 deletions CHANGELOG.md

Large diffs are not rendered by default.

16 changes: 15 additions & 1 deletion config/search/search.j2
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
{% set conversation_upload_enabled = RETRIEVAL_BACKEND == 'foundry_iq' and FOUNDRY_IQ_PATTERN != 'searchIndex' and (FOUNDRY_IQ_CONVERSATION_UPLOAD_ENABLED | default('false') | string | lower) in ['true', '1', 'yes', 'y', 't'] and (FOUNDRY_IQ_CONVERSATION_KNOWLEDGE_SOURCE_NAME | default('', true)) %}
{% set work_iq_enabled = RETRIEVAL_BACKEND == 'foundry_iq' and (WORK_IQ_ENABLED | default('false') | string | lower) in ['true', '1', 'yes', 'y', 't'] and (WORK_IQ_KNOWLEDGE_SOURCE_NAME | default('', true)) %}
{% set fabric_iq_enabled = RETRIEVAL_BACKEND == 'foundry_iq' and (FABRIC_IQ_ENABLED | default('false') | string | lower) in ['true', '1', 'yes', 'y', 't'] and (FABRIC_IQ_KNOWLEDGE_SOURCE_NAME | default('', true)) and (FABRIC_IQ_WORKSPACE_ID | default('', true)) and (FABRIC_IQ_ONTOLOGY_ID | default('', true)) %}
{% set fabric_data_agent_enabled = RETRIEVAL_BACKEND == 'foundry_iq' and (FABRIC_DATA_AGENT_ENABLED | default('false') | string | lower) in ['true', '1', 'yes', 'y', 't'] and (FABRIC_DATA_AGENT_KNOWLEDGE_SOURCE_NAME | default('', true)) and (FABRIC_DATA_AGENT_WORKSPACE_ID | default('', true)) and (FABRIC_DATA_AGENT_DATA_AGENT_ID | default('', true)) %}
{
"indexes": [
{
Expand Down Expand Up @@ -433,6 +434,18 @@
"encryptionKey": null
}
{% endif %}
{% if fabric_data_agent_enabled %},
{
"name": "{{FABRIC_DATA_AGENT_KNOWLEDGE_SOURCE_NAME}}",
"kind": "fabricDataAgent",
"description": "Microsoft Fabric Data Agent knowledge source (queries a Fabric Data Agent in the caller's workspace).",
"fabricDataAgentParameters": {
"workspaceId": "{{FABRIC_DATA_AGENT_WORKSPACE_ID}}",
"dataAgentId": "{{FABRIC_DATA_AGENT_DATA_AGENT_ID}}"
},
"encryptionKey": null
}
{% endif %}
]{% else %}[]{% endif %},
"knowledgeBases": {% if RETRIEVAL_BACKEND == 'foundry_iq' %}[
{
Expand All @@ -445,7 +458,8 @@
{ "name": "{{FOUNDRY_IQ_KNOWLEDGE_SOURCE_NAME}}" }{% if conversation_upload_enabled %},
{ "name": "{{FOUNDRY_IQ_CONVERSATION_KNOWLEDGE_SOURCE_NAME}}" }{% endif %}{% if work_iq_enabled %},
{ "name": "{{WORK_IQ_KNOWLEDGE_SOURCE_NAME}}" }{% endif %}{% if fabric_iq_enabled %},
{ "name": "{{FABRIC_IQ_KNOWLEDGE_SOURCE_NAME}}" }{% endif %}
{ "name": "{{FABRIC_IQ_KNOWLEDGE_SOURCE_NAME}}" }{% endif %}{% if fabric_data_agent_enabled %},
{ "name": "{{FABRIC_DATA_AGENT_KNOWLEDGE_SOURCE_NAME}}" }{% endif %}
],
"models": [],
"encryptionKey": null,
Expand Down
4 changes: 4 additions & 0 deletions config/search/search.settings.j2
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
"FABRIC_IQ_KNOWLEDGE_SOURCE_NAME": "{{FABRIC_IQ_KNOWLEDGE_SOURCE_NAME | default('')}}",
"FABRIC_IQ_WORKSPACE_ID": "{{FABRIC_IQ_WORKSPACE_ID | default('')}}",
"FABRIC_IQ_ONTOLOGY_ID": "{{FABRIC_IQ_ONTOLOGY_ID | default('')}}",
"FABRIC_DATA_AGENT_ENABLED": "{{FABRIC_DATA_AGENT_ENABLED | default('false')}}",
"FABRIC_DATA_AGENT_KNOWLEDGE_SOURCE_NAME": "{{FABRIC_DATA_AGENT_KNOWLEDGE_SOURCE_NAME | default('')}}",
"FABRIC_DATA_AGENT_WORKSPACE_ID": "{{FABRIC_DATA_AGENT_WORKSPACE_ID | default('')}}",
"FABRIC_DATA_AGENT_DATA_AGENT_ID": "{{FABRIC_DATA_AGENT_DATA_AGENT_ID | default('')}}",
{% if GPT_MODEL_INFO | default({}) %}
"GPT_MODEL_DEPLOYMENT_NAME": "{{GPT_MODEL_INFO.deployment_name}}",
"GPT_MODEL_NAME": "{{GPT_MODEL_INFO.model_name}}"
Expand Down
18 changes: 18 additions & 0 deletions config/search/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,24 @@ def is_fabric_iq_enabled(context: dict) -> bool:
)


def is_fabric_data_agent_enabled(context: dict) -> bool:
"""Return True when Fabric Data Agent should be registered as a knowledge source.

Requires the retrieval backend to be Foundry IQ, the
FABRIC_DATA_AGENT_ENABLED switch to be truthy, and all three binding
fields (knowledge source name, Fabric workspace id, Fabric data agent id)
to be set. Fabric Data Agent has no shared service principal, so there is
no admin-consent preflight.
"""
return (
str(context.get("RETRIEVAL_BACKEND") or "").lower() == "foundry_iq"
and is_truthy_setting(context.get("FABRIC_DATA_AGENT_ENABLED"))
and bool(str(context.get("FABRIC_DATA_AGENT_KNOWLEDGE_SOURCE_NAME") or "").strip())
and bool(str(context.get("FABRIC_DATA_AGENT_WORKSPACE_ID") or "").strip())
and bool(str(context.get("FABRIC_DATA_AGENT_DATA_AGENT_ID") or "").strip())
)


def check_work_iq_admin_consent(cred: ChainedTokenCredential) -> Optional[bool]:
"""Soft preflight: check whether the Work IQ service principal has been
consented in the caller's tenant.
Expand Down
66 changes: 66 additions & 0 deletions config/search/tests/test_foundry_iq_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,72 @@ def test_fabric_iq_enabled_adds_fabric_knowledge_source_and_kb_reference(self):
]
self.assertIn("fabric-iq-ks", kb_source_names)

def test_settings_defaults_fabric_data_agent_disabled_and_empty(self):
settings = render_json_template(
"search.settings.j2",
{
"RESOURCE_TOKEN": "abc123",
"SEARCH_SERVICE_QUERY_ENDPOINT": "https://search.search.windows.net",
"AI_FOUNDRY_ACCOUNT_NAME": "aif-abc123",
"RETRIEVAL_BACKEND": "foundry_iq",
},
)
self.assertEqual(settings["FABRIC_DATA_AGENT_ENABLED"], "false")
self.assertEqual(settings["FABRIC_DATA_AGENT_KNOWLEDGE_SOURCE_NAME"], "")
self.assertEqual(settings["FABRIC_DATA_AGENT_WORKSPACE_ID"], "")
self.assertEqual(settings["FABRIC_DATA_AGENT_DATA_AGENT_ID"], "")

def test_fabric_data_agent_disabled_by_default_produces_no_entry(self):
_, context = self._foundry_iq_context()
search_definitions = render_json_template("search.j2", context)
for ks in search_definitions["knowledgeSources"]:
self.assertNotEqual(ks["kind"], "fabricDataAgent")
kb_source_names = [
s["name"] for s in search_definitions["knowledgeBases"][0]["knowledgeSources"]
]
self.assertNotIn("fabric-data-agent-ks", kb_source_names)

def test_fabric_data_agent_enabled_without_binding_fields_produces_no_entry(self):
# Enabled + name but missing workspace and data agent ids must not emit.
_, context = self._foundry_iq_context(
FABRIC_DATA_AGENT_ENABLED="true",
FABRIC_DATA_AGENT_KNOWLEDGE_SOURCE_NAME="fabric-data-agent-ks",
)
search_definitions = render_json_template("search.j2", context)
for ks in search_definitions["knowledgeSources"]:
self.assertNotEqual(ks["kind"], "fabricDataAgent")

def test_fabric_data_agent_enabled_adds_knowledge_source_and_kb_reference(self):
_, context = self._foundry_iq_context(
FABRIC_DATA_AGENT_ENABLED="true",
FABRIC_DATA_AGENT_KNOWLEDGE_SOURCE_NAME="fabric-data-agent-ks",
FABRIC_DATA_AGENT_WORKSPACE_ID="ws-guid-2",
FABRIC_DATA_AGENT_DATA_AGENT_ID="da-guid-2",
)
search_definitions = render_json_template("search.j2", context)

agent_sources = [
ks
for ks in search_definitions["knowledgeSources"]
if ks["kind"] == "fabricDataAgent"
]
self.assertEqual(len(agent_sources), 1)
agent = agent_sources[0]
self.assertEqual(agent["name"], "fabric-data-agent-ks")
self.assertEqual(agent["kind"], "fabricDataAgent")
self.assertIsNone(agent["encryptionKey"])
# Fabric Data Agent is service-managed. It must not carry a filterAddOn.
self.assertNotIn("filterAddOn", agent)
self.assertEqual(
agent["fabricDataAgentParameters"],
{"workspaceId": "ws-guid-2", "dataAgentId": "da-guid-2"},
)

kb_source_names = [
s["name"] for s in search_definitions["knowledgeBases"][0]["knowledgeSources"]
]
self.assertIn("fabric-data-agent-ks", kb_source_names)


class WorkIqPreflightTests(unittest.TestCase):
def test_filter_work_iq_sources_no_op_when_disabled(self):
Expand Down
4 changes: 2 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"tag": "v3.4.1",
"tag": "v3.4.2",
"repo": "https://github.com/azure/gpt-rag.git",
"ailz_tag": "v2.3.0",
"components": [
Expand All @@ -11,7 +11,7 @@
{
"name": "gpt-rag-orchestrator",
"repo": "https://github.com/azure/gpt-rag-orchestrator.git",
"tag": "v3.3.0"
"tag": "v3.4.0"
},
{
"name": "gpt-rag-ingestion",
Expand Down
14 changes: 9 additions & 5 deletions scripts/postProvision.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# postProvision.ps1
# postProvision.ps1

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Expand Down Expand Up @@ -231,12 +231,12 @@ function Set-GptRagAppConfiguration {
-Fallback "srch-$nameSuffix" `
-Filter { -not $_.name.StartsWith('srch-aif') }

# AI Foundry Cognitive account (kind = AIServices) LZ v2.2.0 deploys a single one
# AI Foundry Cognitive account (kind = AIServices) - LZ v2.2.0 deploys a single one
$foundryName = _resolveResource `
-Type 'Microsoft.CognitiveServices/accounts' `
-Fallback "aif-$nameSuffix"

# AI Foundry project (child of foundry account) resolve via CLI
# AI Foundry project (child of foundry account) - resolve via CLI
$foundryProjectName = 'aifoundry-default-project'
try {
$projJson = az cognitiveservices account list-projects -g $resourceGroup -n $foundryName -o json 2>$null
Expand All @@ -260,13 +260,13 @@ function Set-GptRagAppConfiguration {
-Type 'Microsoft.Insights/components' `
-Fallback "appi-$nameSuffix"

# Container apps use the RESOURCE_TOKEN suffix from GPT-RAG's own bicep this is
# Container apps use the RESOURCE_TOKEN suffix from GPT-RAG's own bicep - this is
# stable across naming modes.
$frontendAppName = "ca-$nameSuffix-frontend"
$orchestratorAppName = "ca-$nameSuffix-orchestrator"
$dataIngestAppName = "ca-$nameSuffix-dataingest"

# Cosmos database name introspect the account to get the actual DB name
# Cosmos database name - introspect the account to get the actual DB name
$databaseName = "cosmos-db$nameSuffix"
try {
$dbJson = az cosmosdb sql database list -g $resourceGroup -a $cosmosName -o json 2>$null
Expand Down Expand Up @@ -406,6 +406,10 @@ function Set-GptRagAppConfiguration {
FABRIC_IQ_KNOWLEDGE_SOURCE_NAME = (Get-OptionalEnvValue 'FABRIC_IQ_KNOWLEDGE_SOURCE_NAME' '')
FABRIC_IQ_WORKSPACE_ID = (Get-OptionalEnvValue 'FABRIC_IQ_WORKSPACE_ID' '')
FABRIC_IQ_ONTOLOGY_ID = (Get-OptionalEnvValue 'FABRIC_IQ_ONTOLOGY_ID' '')
FABRIC_DATA_AGENT_ENABLED = (Get-OptionalEnvValue 'FABRIC_DATA_AGENT_ENABLED' 'false')
FABRIC_DATA_AGENT_KNOWLEDGE_SOURCE_NAME = (Get-OptionalEnvValue 'FABRIC_DATA_AGENT_KNOWLEDGE_SOURCE_NAME' '')
FABRIC_DATA_AGENT_WORKSPACE_ID = (Get-OptionalEnvValue 'FABRIC_DATA_AGENT_WORKSPACE_ID' '')
FABRIC_DATA_AGENT_DATA_AGENT_ID = (Get-OptionalEnvValue 'FABRIC_DATA_AGENT_DATA_AGENT_ID' '')
NETWORK_ISOLATION = (Get-OptionalEnvValue 'NETWORK_ISOLATION' 'false')
USE_UAI = (Get-OptionalEnvValue 'USE_UAI' 'false')
USE_CAPP_API_KEY = (Get-OptionalEnvValue 'USE_CAPP_API_KEY' 'false')
Expand Down
Loading