Skip to content

Commit 226e4ef

Browse files
authored
Merge pull request #2256 from asimurka/feature/shields-config
LCORE-3269: Restructure LCORE shield configuration
2 parents 47244fc + e317368 commit 226e4ef

16 files changed

Lines changed: 132 additions & 98 deletions

File tree

docs/devel_doc/openapi.json

Lines changed: 34 additions & 23 deletions
Large diffs are not rendered by default.

src/models/api/requests/query.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class QueryRequest(BaseModel):
2323
generate_topic_summary: Whether to generate topic summary for new conversations.
2424
media_type: The optional media type for response format (application/json or text/plain).
2525
vector_store_ids: The optional list of specific vector store IDs to query for RAG.
26-
shield_ids: The optional list of safety shield IDs to apply.
26+
shield_ids: The optional list of configured shield names to apply.
2727
solr: Optional Solr inline RAG options (mode, filters) or legacy filter-only dict.
2828
"""
2929

@@ -105,9 +105,9 @@ class QueryRequest(BaseModel):
105105

106106
shield_ids: Optional[list[str]] = Field(
107107
None,
108-
description="Optional list of safety shield IDs to apply. "
109-
"If None, all configured shields are used. ",
110-
examples=["llama-guard", "custom-shield"],
108+
description="Optional list of configured shield names to apply. "
109+
"If None, all configured shields are used.",
110+
examples=["topic-guard", "pii-redaction"],
111111
)
112112

113113
solr: Optional[SolrVectorSearchRequest] = Field(

src/models/api/requests/responses_openai.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ class ResponsesRequest(BaseModel):
5757
calls, MCP tools). Defaults to all tools available to the model.
5858
generate_topic_summary: LCORE-specific flag indicating whether to generate a
5959
topic summary for new conversations. Defaults to True.
60-
shield_ids: LCORE-specific list of safety shield IDs to apply. If None, all
61-
configured shields are used.
60+
shield_ids: LCORE-specific list of configured shield names to apply.
61+
If None, all configured shields are used.
6262
solr: Optional Solr inline RAG options (mode, filters) or legacy filter-only dict.
6363
"""
6464

src/models/api/responses/successful/catalog.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ class ShieldsResponse(AbstractSuccessfulResponse):
9191
"shields": [
9292
{
9393
"name": "question-validity",
94-
"type": "question_validity",
94+
"provider_id": "question_validity",
95+
"type": "shield",
9596
"config": {
9697
"model_id": "openai/gpt-4o-mini",
9798
"model_prompt": "Is this question valid?",
@@ -102,7 +103,8 @@ class ShieldsResponse(AbstractSuccessfulResponse):
102103
},
103104
{
104105
"name": "pii-redaction",
105-
"type": "redaction",
106+
"provider_id": "redaction",
107+
"type": "shield",
106108
"config": {
107109
"rules": [
108110
{

src/models/common/shields.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,20 @@
1010
class CatalogShield(BaseModel):
1111
"""Shield entry in the ``/shields`` catalog response.
1212
13-
Mirrors the LCS-owned ``ShieldConfiguration`` shape (name / type / config).
13+
Attributes:
14+
name: Unique, user-facing name identifying this shield instance.
15+
provider_id: Shield provider / type discriminator.
16+
type: Catalog entry type; always shield.
17+
config: Type-specific shield configuration.
1418
"""
1519

1620
name: str = Field(description="Unique, user-facing name of the shield instance")
17-
type: Literal["question_validity", "redaction"] = Field(
18-
description="Shield type discriminator",
21+
provider_id: Literal["question_validity", "redaction"] = Field(
22+
description="Shield provider / type discriminator",
23+
)
24+
type: Literal["shield"] = Field(
25+
default="shield",
26+
description="Catalog entry type; always shield",
1927
)
2028
config: dict[str, Any] = Field(
2129
description="Type-specific shield configuration",

src/models/config.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2595,7 +2595,7 @@ class QuestionValidityShieldConfiguration(ConfigurationBase):
25952595
25962596
Attributes:
25972597
name: Unique, user-facing name identifying this shield instance.
2598-
type: Discriminator identifying this as a question-validity shield.
2598+
provider_id: Discriminator identifying this as a question-validity shield.
25992599
config: Question-validity-specific configuration.
26002600
"""
26012601

@@ -2605,9 +2605,9 @@ class QuestionValidityShieldConfiguration(ConfigurationBase):
26052605
description="Unique, user-facing name identifying this shield instance.",
26062606
)
26072607

2608-
type: Literal["question_validity"] = Field(
2608+
provider_id: Literal["question_validity"] = Field(
26092609
...,
2610-
title="Shield type",
2610+
title="Shield provider id",
26112611
description="Discriminator identifying this as a question-validity shield.",
26122612
)
26132613

@@ -2623,7 +2623,7 @@ class RedactionShieldConfiguration(ConfigurationBase):
26232623
26242624
Attributes:
26252625
name: Unique, user-facing name identifying this shield instance.
2626-
type: Discriminator identifying this as a redaction shield.
2626+
provider_id: Discriminator identifying this as a redaction shield.
26272627
config: Redaction-specific configuration.
26282628
"""
26292629

@@ -2633,9 +2633,9 @@ class RedactionShieldConfiguration(ConfigurationBase):
26332633
description="Unique, user-facing name identifying this shield instance.",
26342634
)
26352635

2636-
type: Literal["redaction"] = Field(
2636+
provider_id: Literal["redaction"] = Field(
26372637
...,
2638-
title="Shield type",
2638+
title="Shield provider id",
26392639
description="Discriminator identifying this as a redaction shield.",
26402640
)
26412641

@@ -2648,11 +2648,11 @@ class RedactionShieldConfiguration(ConfigurationBase):
26482648

26492649
ShieldConfiguration = Annotated[
26502650
QuestionValidityShieldConfiguration | RedactionShieldConfiguration,
2651-
Field(discriminator="type"),
2651+
Field(discriminator="provider_id"),
26522652
]
26532653
"""Configuration for a single named guardrail shield (question validity or redaction).
26542654
2655-
A discriminated union on ``type``: Pydantic selects
2655+
A discriminated union on ``provider_id``: Pydantic selects
26562656
``QuestionValidityShieldConfiguration`` or ``RedactionShieldConfiguration``
26572657
and validates ``config`` against the matching model.
26582658
"""
@@ -2847,9 +2847,9 @@ class Configuration(ConfigurationBase):
28472847
default_factory=list,
28482848
title="Shields configuration",
28492849
description="List of pydantic-ai-lightspeed agent guardrail shields "
2850-
"(question validity and PII redaction). Each entry has a unique 'name', "
2851-
"a 'type' ('question_validity' or 'redaction'), and a type-specific "
2852-
"'config'.",
2850+
"(question validity and PII redaction). Each entry has a unique "
2851+
"'name', a 'provider_id' ('question_validity' or 'redaction'), "
2852+
"and a type-specific 'config'.",
28532853
)
28542854

28552855
@model_validator(mode="after")

src/utils/shields.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -133,22 +133,20 @@ def get_shields_for_request(
133133
shields: list[ShieldConfiguration],
134134
shield_ids: Optional[list[str]] = None,
135135
) -> list[ShieldConfiguration]:
136-
"""Return configured shields, optionally filtered by request ``shield_ids``.
137-
138-
Shield identifiers in the request map to each shield's configured ``name``.
136+
"""Return configured shields, optionally filtered by request shield_ids.
139137
140138
Args:
141139
shields: Configured LCS shields.
142-
shield_ids: Optional list of shield names. If ``None``, all ``shields``
143-
are returned. An empty list skips all shields. Otherwise only
144-
shields whose ``name`` is in this list are returned.
140+
shield_ids: Optional list of shield names. If None, all shields are
141+
returned. An empty list skips all shields. Otherwise only shields
142+
whose name is in this list are returned.
145143
146144
Returns:
147145
list[ShieldConfiguration]: Shield configurations to run for this request.
148146
149147
Raises:
150-
HTTPException: 404 if ``shield_ids`` is provided and any requested
151-
shield name is not present in ``shields``.
148+
HTTPException: 404 if shield_ids is provided and any requested shield
149+
name is not present in shields.
152150
"""
153151
if shield_ids is None:
154152
return list(shields)
@@ -157,8 +155,8 @@ def get_shields_for_request(
157155
return []
158156

159157
requested = set(shield_ids)
160-
configured_ids = {shield.name for shield in shields}
161-
missing = requested - configured_ids
158+
configured_names = {shield.name for shield in shields}
159+
missing = requested - configured_names
162160
if missing:
163161
response = NotFoundResponse(
164162
resource=f"Shield{'s' if len(missing) > 1 else ''}",

tests/e2e/configuration/library-mode/lightspeed-stack.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,12 @@ byok_rag:
3535
rag:
3636
tool:
3737
- e2e-test-docs
38+
39+
shields:
40+
- name: pii-redaction
41+
provider_id: redaction
42+
config:
43+
rules:
44+
- pattern: '\d+'
45+
replacement: '[NUM]'
46+

tests/e2e/configuration/server-mode/lightspeed-stack.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,12 @@ byok_rag:
3232

3333
rag:
3434
tool:
35-
- e2e-test-docs
35+
- e2e-test-docs
36+
37+
shields:
38+
- name: pii-redaction
39+
provider_id: redaction
40+
config:
41+
rules:
42+
- pattern: '\d+'
43+
replacement: '[NUM]'

tests/e2e/features/info.feature

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ Feature: Info tests
2121
And The body of the response has proper name Lightspeed Core Service (LCS) and version 0.6.0rc2
2222
And The body of the response has llama-stack version 1.0.2
2323

24-
@skip
2524
Scenario: Check if shields endpoint is working
2625
When I access REST API endpoint "shields" using HTTP GET method
2726
Then The status code of the response is 200

0 commit comments

Comments
 (0)