deepintshield 2.3.0__tar.gz → 2.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {deepintshield-2.3.0/src/deepintshield.egg-info → deepintshield-2.5.0}/PKG-INFO +34 -34
- {deepintshield-2.3.0 → deepintshield-2.5.0}/README.md +32 -32
- {deepintshield-2.3.0 → deepintshield-2.5.0}/pyproject.toml +2 -2
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/__init__.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/_gemini_cache.py +8 -8
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/_prompt_cache.py +5 -5
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/__init__.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/credentials/__init__.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/credentials/base.py +2 -2
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/credentials/entra.py +4 -4
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/credentials/oidc.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/credentials/zeroid.py +2 -2
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/decorators.py +3 -3
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/enforcement.py +5 -5
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/engine.py +7 -7
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/errors.py +3 -3
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/gate.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/integrations/_common.py +3 -3
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/integrations/autogen.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/integrations/crewai.py +2 -2
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/integrations/langchain.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/integrations/langgraph.py +7 -7
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/integrations/litellm.py +2 -2
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/integrations/llamaindex.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/integrations/openai_agents.py +3 -3
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/integrations/pydanticai.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/manifest.py +5 -5
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/obligations.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/surface.py +15 -15
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/types.py +8 -8
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/client.py +3 -3
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/errors.py +5 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/frameworks/__init__.py +2 -2
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/frameworks/autogen.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/frameworks/crewai.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/frameworks/langgraph.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/frameworks/llamaindex.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/frameworks/openai_agents.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/frameworks/pydanticai.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/mcp/__init__.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/mcp/adapters/__init__.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/mcp/adapters/langchain.py +2 -2
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/mcp/adapters/openai.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/mcp/client.py +2 -2
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/providers/anthropic.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/providers/genai.py +3 -3
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/providers/openai.py +1 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/transport.py +6 -6
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/types.py +1 -1
- deepintshield-2.5.0/src/deepintshield/version.py +1 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0/src/deepintshield.egg-info}/PKG-INFO +34 -34
- {deepintshield-2.3.0 → deepintshield-2.5.0}/tests/test_gemini_cache.py +2 -2
- deepintshield-2.3.0/src/deepintshield/version.py +0 -1
- {deepintshield-2.3.0 → deepintshield-2.5.0}/LICENSE +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/setup.cfg +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agent.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/integrations/__init__.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/config.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/mcp/adapters/anthropic.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/mcp/tool.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/providers/__init__.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/providers/bedrock.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/providers/langchain.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/providers/langgraph.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/providers/litellm.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/providers/pydanticai.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/rag.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield.egg-info/SOURCES.txt +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield.egg-info/dependency_links.txt +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield.egg-info/requires.txt +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield.egg-info/top_level.txt +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/tests/test_agent.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/tests/test_agentic.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/tests/test_agentic_langchain.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/tests/test_client.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/tests/test_config.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/tests/test_errors.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/tests/test_prompt_cache.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/tests/test_providers.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/tests/test_rag.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/tests/test_rag_guard.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/tests/test_transport.py +0 -0
- {deepintshield-2.3.0 → deepintshield-2.5.0}/tests/test_types.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepintshield
|
|
3
|
-
Version: 2.
|
|
4
|
-
Summary: Unified Python SDK for routing chat, RAG, agentic tool-gating, identity, and MCP traffic through DeepintShield
|
|
3
|
+
Version: 2.5.0
|
|
4
|
+
Summary: Unified Python SDK for routing chat, RAG, agentic tool-gating, identity, and MCP traffic through DeepintShield - drop-in across the top agentic frameworks.
|
|
5
5
|
Author: DeepintShield
|
|
6
6
|
License: Apache-2.0
|
|
7
7
|
Project-URL: Homepage, https://app.deepintshield.com
|
|
@@ -71,7 +71,7 @@ Dynamic: license-file
|
|
|
71
71
|
|
|
72
72
|
# deepintshield
|
|
73
73
|
|
|
74
|
-
Unified Python SDK for DeepintShield
|
|
74
|
+
Unified Python SDK for DeepintShield - one import, any provider, any agent framework.
|
|
75
75
|
|
|
76
76
|
`deepintshield` lets you keep writing idiomatic OpenAI / Anthropic / Bedrock /
|
|
77
77
|
Google GenAI code **and** native agent-framework code (LangGraph, CrewAI,
|
|
@@ -79,20 +79,20 @@ OpenAI Agents SDK, LlamaIndex, AutoGen, PydanticAI) while automatically routing
|
|
|
79
79
|
traffic through the DeepintShield gateway for guardrails, RAG filtering, agentic
|
|
80
80
|
tool control, and agent identity.
|
|
81
81
|
|
|
82
|
-
You pass **only two things
|
|
83
|
-
the Entra / ZeroID / OIDC identity binding, tenant, scopes, and policy
|
|
82
|
+
You pass **only two things - a virtual key and a base URL.** Everything else -
|
|
83
|
+
the Entra / ZeroID / OIDC identity binding, tenant, scopes, and policy - is
|
|
84
84
|
discovered from the gateway automatically. No identity GUIDs ever appear in your
|
|
85
85
|
code.
|
|
86
86
|
|
|
87
87
|
Protection comes in two layers:
|
|
88
88
|
|
|
89
|
-
- **Transparent**
|
|
89
|
+
- **Transparent** - point any framework's *native* client at the gateway
|
|
90
90
|
(`base_url` + a one-line header injector). Chat, embeddings, input/output and
|
|
91
91
|
RAG-prompt guardrails, observability and identity all run server-side with
|
|
92
92
|
**zero changes** to your agent code. Works for Python frameworks *and* any
|
|
93
93
|
OpenAI-compatible platform (n8n, Flowise, Dify, raw HTTP).
|
|
94
|
-
- **Enforcement**
|
|
95
|
-
MASK / human approval) and **chunk-level RAG filtering**
|
|
94
|
+
- **Enforcement** - one wrapping line adds local **tool gating** (DENY /
|
|
95
|
+
MASK / human approval) and **chunk-level RAG filtering** - the parts that
|
|
96
96
|
physically can't be done at the wire because the tool runs in your process.
|
|
97
97
|
|
|
98
98
|
Traffic defaults to **`https://app.deepintshield.com`**. Override the gateway
|
|
@@ -126,7 +126,7 @@ pip install 'deepintshield[all]' # everything
|
|
|
126
126
|
|
|
127
127
|
```bash
|
|
128
128
|
export DEEPINTSHIELD_VIRTUAL_KEY="sk-..."
|
|
129
|
-
# Optional
|
|
129
|
+
# Optional - point at a self-hosted or staging gateway.
|
|
130
130
|
export DEEPINTSHIELD_BASE_URL="https://gateway.example.com"
|
|
131
131
|
```
|
|
132
132
|
|
|
@@ -251,7 +251,7 @@ allowed, raw = shield.rag.filter(query="What's the badge rule?", chunks=chunks)
|
|
|
251
251
|
### Guard a framework retriever (post-retrieval, one line)
|
|
252
252
|
|
|
253
253
|
Wrap any LangChain / LlamaIndex retriever so unauthorised chunks are dropped
|
|
254
|
-
*after* retrieval and *before* they reach the LLM
|
|
254
|
+
*after* retrieval and *before* they reach the LLM - ACL/provenance filtering
|
|
255
255
|
your retriever can't do itself:
|
|
256
256
|
|
|
257
257
|
```python
|
|
@@ -269,7 +269,7 @@ embedder.embed_query("text") # raises if the gatewa
|
|
|
269
269
|
```
|
|
270
270
|
|
|
271
271
|
> If you obtain your embedder from a framework binder (below), input-side
|
|
272
|
-
> screening already happens server-side
|
|
272
|
+
> screening already happens server-side - `guard_embedder` is for embedders you
|
|
273
273
|
> don't route through the gateway.
|
|
274
274
|
|
|
275
275
|
---
|
|
@@ -292,7 +292,7 @@ shield.autogen().model_client("gpt-4o-mini") # OpenAIChatCompletionClient
|
|
|
292
292
|
shield.pydanticai().model("gpt-4o-mini")
|
|
293
293
|
```
|
|
294
294
|
|
|
295
|
-
Framework-agnostic primitives
|
|
295
|
+
Framework-agnostic primitives - wire any SDK, in any language-compatible
|
|
296
296
|
client, by hand:
|
|
297
297
|
|
|
298
298
|
```python
|
|
@@ -302,23 +302,23 @@ headers = shield.create_headers(provider="anthropic") # Portkey-style header i
|
|
|
302
302
|
```
|
|
303
303
|
|
|
304
304
|
> **Universal:** anything that speaks OpenAI-compatible HTTP gets transparent
|
|
305
|
-
> protection with *zero code*
|
|
305
|
+
> protection with *zero code* - set the Base URL to `shield.endpoint("openai")`
|
|
306
306
|
> and the key to your VK. That covers n8n, Flowise, Dify and raw HTTP, not just
|
|
307
307
|
> Python.
|
|
308
308
|
|
|
309
309
|
## Agentic tool enforcement
|
|
310
310
|
|
|
311
311
|
The part a base URL can't do: gate **local** tool execution. Each gated call
|
|
312
|
-
runs `decide()` first and the verdict maps to a Python outcome
|
|
312
|
+
runs `decide()` first and the verdict maps to a Python outcome - `ALLOW` runs
|
|
313
313
|
the body, `MASK` redacts PII kwargs, `REQUIRE_APPROVAL` blocks for a human, and
|
|
314
314
|
`DENY` raises `GuardrailDenied`.
|
|
315
315
|
|
|
316
|
-
### Zero extra code
|
|
316
|
+
### Zero extra code - enforcement is automatic and non-bypassable
|
|
317
317
|
|
|
318
318
|
The moment you construct `DeepintShield(...)`, the SDK installs guards for every
|
|
319
319
|
agent framework you've imported (LangGraph, CrewAI, LlamaIndex, AutoGen,
|
|
320
320
|
PydanticAI, the OpenAI Agents SDK, LiteLLM). After that, **compiling / building
|
|
321
|
-
an agent yields an already-governed object**
|
|
321
|
+
an agent yields an already-governed object** - you can't forget to gate it and
|
|
322
322
|
you can't bypass it by invoking the un-governed one (there isn't one).
|
|
323
323
|
|
|
324
324
|
```python
|
|
@@ -334,7 +334,7 @@ g = StateGraph(State)
|
|
|
334
334
|
g.add_node("read_step", crm_read)
|
|
335
335
|
g.add_node("admin_step", admin_grant)
|
|
336
336
|
...
|
|
337
|
-
app = g.compile() # auto-governed
|
|
337
|
+
app = g.compile() # auto-governed - every node now gated by the PDP
|
|
338
338
|
|
|
339
339
|
app.invoke({...}) # a DENY raises GuardrailDenied before the node runs
|
|
340
340
|
```
|
|
@@ -345,32 +345,32 @@ If you import a framework *after* building the client, call
|
|
|
345
345
|
> **Security follows the implementation, not the label.** Each node/tool is
|
|
346
346
|
> governed by its **function name** (`crm_read`), not the node label
|
|
347
347
|
> (`read_step`), and the decision is bound to a fingerprint of the function's
|
|
348
|
-
> **source**
|
|
348
|
+
> **source** - so editing the body is detected and policies target `crm_read`.
|
|
349
349
|
>
|
|
350
350
|
> **Trust boundary:** these client guards are cooperative defense-in-depth. A
|
|
351
351
|
> determined process can un-patch them or call a tool's raw function, so the
|
|
352
352
|
> gateway (MCP / LLM in the call path) remains the authoritative boundary.
|
|
353
353
|
|
|
354
|
-
### `govern()`
|
|
354
|
+
### `govern()` - register + threat-scan + instrument (explicit, idempotent)
|
|
355
355
|
|
|
356
356
|
`govern()` does everything the auto-guard does, explicitly: it **describes** the
|
|
357
357
|
agent's declared tool surface, **registers** that blueprint with the server
|
|
358
|
-
(which **threat-scans each tool's source** for RCE / shell-out / exfiltration
|
|
359
|
-
OWASP Agentic **T11 / T17**
|
|
358
|
+
(which **threat-scans each tool's source** for RCE / shell-out / exfiltration -
|
|
359
|
+
OWASP Agentic **T11 / T17** - server-side, ZDR), and **instruments** every call.
|
|
360
360
|
|
|
361
361
|
```python
|
|
362
|
-
app = shield.agentic.govern(app) # idempotent
|
|
362
|
+
app = shield.agentic.govern(app) # idempotent - safe alongside the auto-guard
|
|
363
363
|
```
|
|
364
364
|
|
|
365
365
|
A tool whose source scans malicious is flagged (Agentic → Findings) and, when
|
|
366
|
-
the workspace enables **Enforce code threat** (Rollout), denied
|
|
366
|
+
the workspace enables **Enforce code threat** (Rollout), denied - even if a
|
|
367
367
|
policy would otherwise allow it. A tool called but never declared shows up as
|
|
368
368
|
**ASI04 drift** under Agentic → Discovery.
|
|
369
369
|
|
|
370
|
-
### `guard()`
|
|
370
|
+
### `guard()` - LangChain callback / in-place instrument
|
|
371
371
|
|
|
372
372
|
`shield.agentic.guard()` (no argument) returns a native LangChain callback
|
|
373
|
-
handler; attaching it once gates *every* tool the agent calls
|
|
373
|
+
handler; attaching it once gates *every* tool the agent calls - the framework
|
|
374
374
|
supplies the tool name and the **gateway resolves the tier, policy, recovery
|
|
375
375
|
cost and identity server-side**.
|
|
376
376
|
|
|
@@ -382,7 +382,7 @@ agent_executor.invoke({"input": "…"}, config={"callbacks": [guard]})
|
|
|
382
382
|
`guard(target)` auto-detects and instruments a framework object in place:
|
|
383
383
|
|
|
384
384
|
```python
|
|
385
|
-
shield.agentic.guard(compiled_graph) # LangGraph
|
|
385
|
+
shield.agentic.guard(compiled_graph) # LangGraph - gate every tool node
|
|
386
386
|
shield.agentic.guard(crewai_tools) # CrewAI BaseTools
|
|
387
387
|
shield.agentic.guard(openai_agent) # OpenAI Agents FunctionTools
|
|
388
388
|
shield.agentic.guard(pydantic_agent) # PydanticAI agent
|
|
@@ -435,7 +435,7 @@ When the virtual key is bound to an identity provider, the SDK auto-discovers
|
|
|
435
435
|
the binding (`GET /api/agentic-security/vk-credential-info`), selects the right
|
|
436
436
|
credential (Entra Agent ID FIC / ZeroID RFC 8693 / generic OIDC), and attaches a
|
|
437
437
|
fresh `X-Agent-Token` on every decision. On Azure compute the Managed Identity
|
|
438
|
-
is detected automatically
|
|
438
|
+
is detected automatically - **no GUIDs, authority, or scopes in your code.**
|
|
439
439
|
|
|
440
440
|
```python
|
|
441
441
|
info = shield.agentic.credential_info # ops visibility into the binding
|
|
@@ -473,7 +473,7 @@ app = graph.compile()
|
|
|
473
473
|
## Multimodal guardrails (transparent)
|
|
474
474
|
|
|
475
475
|
Image generation, image edits, audio (TTS / transcription), video, embedding and
|
|
476
|
-
rerank requests are guarded **at the gateway**
|
|
476
|
+
rerank requests are guarded **at the gateway** - no SDK changes and no extra code.
|
|
477
477
|
Keep using the native provider SDKs through DeepIntShield; when the operator
|
|
478
478
|
enables `GUARDRAILS_MULTIMODAL`, the gateway evaluates the text these requests
|
|
479
479
|
already carry (image/TTS/video prompts, transcripts) and the binary artifacts
|
|
@@ -482,7 +482,7 @@ themselves, blocking or flagging per your policies.
|
|
|
482
482
|
```python
|
|
483
483
|
client = shield.openai()
|
|
484
484
|
|
|
485
|
-
# Guarded automatically
|
|
485
|
+
# Guarded automatically - the image prompt is evaluated before generation.
|
|
486
486
|
img = client.images.generate(model="gpt-image-1", prompt="a serene mountain lake")
|
|
487
487
|
|
|
488
488
|
# A blocked prompt surfaces as the provider SDK's normal HTTP error:
|
|
@@ -501,7 +501,7 @@ enforcing (`sync`) or observe-only (`shadow`).
|
|
|
501
501
|
|
|
502
502
|
## MCP
|
|
503
503
|
|
|
504
|
-
Generic MCP support
|
|
504
|
+
Generic MCP support - works with any server connected to your DeepintShield
|
|
505
505
|
gateway. No per-server SDK code; the same `Tool` / `MCPClient` API serves
|
|
506
506
|
DeepWiki, Context7, GitHub MCP, an internal one, and so on.
|
|
507
507
|
|
|
@@ -596,7 +596,7 @@ print(AgentExecutor(agent=agent, tools=mcp_tools).invoke(
|
|
|
596
596
|
)["output"])
|
|
597
597
|
```
|
|
598
598
|
|
|
599
|
-
LangGraph reuses the same `mcp_tools` list
|
|
599
|
+
LangGraph reuses the same `mcp_tools` list - drop them into a `ToolNode`.
|
|
600
600
|
|
|
601
601
|
---
|
|
602
602
|
|
|
@@ -605,16 +605,16 @@ LangGraph reuses the same `mcp_tools` list — drop them into a `ToolNode`.
|
|
|
605
605
|
The SDK automatically participates in the gateway's two cost-reduction layers
|
|
606
606
|
(both controlled by workspace switches under **Cost Optimization**):
|
|
607
607
|
|
|
608
|
-
- **Provider prompt caching**
|
|
608
|
+
- **Provider prompt caching** - every chat client returned by `shield.openai()`,
|
|
609
609
|
`shield.anthropic()`, etc. ships an `httpx` request hook that injects
|
|
610
610
|
Anthropic `cache_control` markers and an OpenAI `prompt_cache_key` so the
|
|
611
611
|
provider reuses KV state for the static prompt prefix. Cached tokens come
|
|
612
612
|
back at the provider's reduced rate (50% off OpenAI, 90% off Anthropic).
|
|
613
|
-
- **Gemini context caching**
|
|
613
|
+
- **Gemini context caching** - opt in with `shield.genai_cached()` (drop-in
|
|
614
614
|
for `shield.genai()`). The wrapper manages the `cachedContents` resource
|
|
615
615
|
lifecycle behind the scenes; the first call with a new static prefix runs
|
|
616
616
|
normally and the next call within the TTL window reuses the cache.
|
|
617
|
-
- **Semantic caching**
|
|
617
|
+
- **Semantic caching** - runs on the gateway; short-circuits requests whose
|
|
618
618
|
embeddings match a previous response within the configured similarity
|
|
619
619
|
threshold. The SDK doesn't need any code change to benefit; results flow
|
|
620
620
|
back through the normal API.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# deepintshield
|
|
2
2
|
|
|
3
|
-
Unified Python SDK for DeepintShield
|
|
3
|
+
Unified Python SDK for DeepintShield - one import, any provider, any agent framework.
|
|
4
4
|
|
|
5
5
|
`deepintshield` lets you keep writing idiomatic OpenAI / Anthropic / Bedrock /
|
|
6
6
|
Google GenAI code **and** native agent-framework code (LangGraph, CrewAI,
|
|
@@ -8,20 +8,20 @@ OpenAI Agents SDK, LlamaIndex, AutoGen, PydanticAI) while automatically routing
|
|
|
8
8
|
traffic through the DeepintShield gateway for guardrails, RAG filtering, agentic
|
|
9
9
|
tool control, and agent identity.
|
|
10
10
|
|
|
11
|
-
You pass **only two things
|
|
12
|
-
the Entra / ZeroID / OIDC identity binding, tenant, scopes, and policy
|
|
11
|
+
You pass **only two things - a virtual key and a base URL.** Everything else -
|
|
12
|
+
the Entra / ZeroID / OIDC identity binding, tenant, scopes, and policy - is
|
|
13
13
|
discovered from the gateway automatically. No identity GUIDs ever appear in your
|
|
14
14
|
code.
|
|
15
15
|
|
|
16
16
|
Protection comes in two layers:
|
|
17
17
|
|
|
18
|
-
- **Transparent**
|
|
18
|
+
- **Transparent** - point any framework's *native* client at the gateway
|
|
19
19
|
(`base_url` + a one-line header injector). Chat, embeddings, input/output and
|
|
20
20
|
RAG-prompt guardrails, observability and identity all run server-side with
|
|
21
21
|
**zero changes** to your agent code. Works for Python frameworks *and* any
|
|
22
22
|
OpenAI-compatible platform (n8n, Flowise, Dify, raw HTTP).
|
|
23
|
-
- **Enforcement**
|
|
24
|
-
MASK / human approval) and **chunk-level RAG filtering**
|
|
23
|
+
- **Enforcement** - one wrapping line adds local **tool gating** (DENY /
|
|
24
|
+
MASK / human approval) and **chunk-level RAG filtering** - the parts that
|
|
25
25
|
physically can't be done at the wire because the tool runs in your process.
|
|
26
26
|
|
|
27
27
|
Traffic defaults to **`https://app.deepintshield.com`**. Override the gateway
|
|
@@ -55,7 +55,7 @@ pip install 'deepintshield[all]' # everything
|
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
57
|
export DEEPINTSHIELD_VIRTUAL_KEY="sk-..."
|
|
58
|
-
# Optional
|
|
58
|
+
# Optional - point at a self-hosted or staging gateway.
|
|
59
59
|
export DEEPINTSHIELD_BASE_URL="https://gateway.example.com"
|
|
60
60
|
```
|
|
61
61
|
|
|
@@ -180,7 +180,7 @@ allowed, raw = shield.rag.filter(query="What's the badge rule?", chunks=chunks)
|
|
|
180
180
|
### Guard a framework retriever (post-retrieval, one line)
|
|
181
181
|
|
|
182
182
|
Wrap any LangChain / LlamaIndex retriever so unauthorised chunks are dropped
|
|
183
|
-
*after* retrieval and *before* they reach the LLM
|
|
183
|
+
*after* retrieval and *before* they reach the LLM - ACL/provenance filtering
|
|
184
184
|
your retriever can't do itself:
|
|
185
185
|
|
|
186
186
|
```python
|
|
@@ -198,7 +198,7 @@ embedder.embed_query("text") # raises if the gatewa
|
|
|
198
198
|
```
|
|
199
199
|
|
|
200
200
|
> If you obtain your embedder from a framework binder (below), input-side
|
|
201
|
-
> screening already happens server-side
|
|
201
|
+
> screening already happens server-side - `guard_embedder` is for embedders you
|
|
202
202
|
> don't route through the gateway.
|
|
203
203
|
|
|
204
204
|
---
|
|
@@ -221,7 +221,7 @@ shield.autogen().model_client("gpt-4o-mini") # OpenAIChatCompletionClient
|
|
|
221
221
|
shield.pydanticai().model("gpt-4o-mini")
|
|
222
222
|
```
|
|
223
223
|
|
|
224
|
-
Framework-agnostic primitives
|
|
224
|
+
Framework-agnostic primitives - wire any SDK, in any language-compatible
|
|
225
225
|
client, by hand:
|
|
226
226
|
|
|
227
227
|
```python
|
|
@@ -231,23 +231,23 @@ headers = shield.create_headers(provider="anthropic") # Portkey-style header i
|
|
|
231
231
|
```
|
|
232
232
|
|
|
233
233
|
> **Universal:** anything that speaks OpenAI-compatible HTTP gets transparent
|
|
234
|
-
> protection with *zero code*
|
|
234
|
+
> protection with *zero code* - set the Base URL to `shield.endpoint("openai")`
|
|
235
235
|
> and the key to your VK. That covers n8n, Flowise, Dify and raw HTTP, not just
|
|
236
236
|
> Python.
|
|
237
237
|
|
|
238
238
|
## Agentic tool enforcement
|
|
239
239
|
|
|
240
240
|
The part a base URL can't do: gate **local** tool execution. Each gated call
|
|
241
|
-
runs `decide()` first and the verdict maps to a Python outcome
|
|
241
|
+
runs `decide()` first and the verdict maps to a Python outcome - `ALLOW` runs
|
|
242
242
|
the body, `MASK` redacts PII kwargs, `REQUIRE_APPROVAL` blocks for a human, and
|
|
243
243
|
`DENY` raises `GuardrailDenied`.
|
|
244
244
|
|
|
245
|
-
### Zero extra code
|
|
245
|
+
### Zero extra code - enforcement is automatic and non-bypassable
|
|
246
246
|
|
|
247
247
|
The moment you construct `DeepintShield(...)`, the SDK installs guards for every
|
|
248
248
|
agent framework you've imported (LangGraph, CrewAI, LlamaIndex, AutoGen,
|
|
249
249
|
PydanticAI, the OpenAI Agents SDK, LiteLLM). After that, **compiling / building
|
|
250
|
-
an agent yields an already-governed object**
|
|
250
|
+
an agent yields an already-governed object** - you can't forget to gate it and
|
|
251
251
|
you can't bypass it by invoking the un-governed one (there isn't one).
|
|
252
252
|
|
|
253
253
|
```python
|
|
@@ -263,7 +263,7 @@ g = StateGraph(State)
|
|
|
263
263
|
g.add_node("read_step", crm_read)
|
|
264
264
|
g.add_node("admin_step", admin_grant)
|
|
265
265
|
...
|
|
266
|
-
app = g.compile() # auto-governed
|
|
266
|
+
app = g.compile() # auto-governed - every node now gated by the PDP
|
|
267
267
|
|
|
268
268
|
app.invoke({...}) # a DENY raises GuardrailDenied before the node runs
|
|
269
269
|
```
|
|
@@ -274,32 +274,32 @@ If you import a framework *after* building the client, call
|
|
|
274
274
|
> **Security follows the implementation, not the label.** Each node/tool is
|
|
275
275
|
> governed by its **function name** (`crm_read`), not the node label
|
|
276
276
|
> (`read_step`), and the decision is bound to a fingerprint of the function's
|
|
277
|
-
> **source**
|
|
277
|
+
> **source** - so editing the body is detected and policies target `crm_read`.
|
|
278
278
|
>
|
|
279
279
|
> **Trust boundary:** these client guards are cooperative defense-in-depth. A
|
|
280
280
|
> determined process can un-patch them or call a tool's raw function, so the
|
|
281
281
|
> gateway (MCP / LLM in the call path) remains the authoritative boundary.
|
|
282
282
|
|
|
283
|
-
### `govern()`
|
|
283
|
+
### `govern()` - register + threat-scan + instrument (explicit, idempotent)
|
|
284
284
|
|
|
285
285
|
`govern()` does everything the auto-guard does, explicitly: it **describes** the
|
|
286
286
|
agent's declared tool surface, **registers** that blueprint with the server
|
|
287
|
-
(which **threat-scans each tool's source** for RCE / shell-out / exfiltration
|
|
288
|
-
OWASP Agentic **T11 / T17**
|
|
287
|
+
(which **threat-scans each tool's source** for RCE / shell-out / exfiltration -
|
|
288
|
+
OWASP Agentic **T11 / T17** - server-side, ZDR), and **instruments** every call.
|
|
289
289
|
|
|
290
290
|
```python
|
|
291
|
-
app = shield.agentic.govern(app) # idempotent
|
|
291
|
+
app = shield.agentic.govern(app) # idempotent - safe alongside the auto-guard
|
|
292
292
|
```
|
|
293
293
|
|
|
294
294
|
A tool whose source scans malicious is flagged (Agentic → Findings) and, when
|
|
295
|
-
the workspace enables **Enforce code threat** (Rollout), denied
|
|
295
|
+
the workspace enables **Enforce code threat** (Rollout), denied - even if a
|
|
296
296
|
policy would otherwise allow it. A tool called but never declared shows up as
|
|
297
297
|
**ASI04 drift** under Agentic → Discovery.
|
|
298
298
|
|
|
299
|
-
### `guard()`
|
|
299
|
+
### `guard()` - LangChain callback / in-place instrument
|
|
300
300
|
|
|
301
301
|
`shield.agentic.guard()` (no argument) returns a native LangChain callback
|
|
302
|
-
handler; attaching it once gates *every* tool the agent calls
|
|
302
|
+
handler; attaching it once gates *every* tool the agent calls - the framework
|
|
303
303
|
supplies the tool name and the **gateway resolves the tier, policy, recovery
|
|
304
304
|
cost and identity server-side**.
|
|
305
305
|
|
|
@@ -311,7 +311,7 @@ agent_executor.invoke({"input": "…"}, config={"callbacks": [guard]})
|
|
|
311
311
|
`guard(target)` auto-detects and instruments a framework object in place:
|
|
312
312
|
|
|
313
313
|
```python
|
|
314
|
-
shield.agentic.guard(compiled_graph) # LangGraph
|
|
314
|
+
shield.agentic.guard(compiled_graph) # LangGraph - gate every tool node
|
|
315
315
|
shield.agentic.guard(crewai_tools) # CrewAI BaseTools
|
|
316
316
|
shield.agentic.guard(openai_agent) # OpenAI Agents FunctionTools
|
|
317
317
|
shield.agentic.guard(pydantic_agent) # PydanticAI agent
|
|
@@ -364,7 +364,7 @@ When the virtual key is bound to an identity provider, the SDK auto-discovers
|
|
|
364
364
|
the binding (`GET /api/agentic-security/vk-credential-info`), selects the right
|
|
365
365
|
credential (Entra Agent ID FIC / ZeroID RFC 8693 / generic OIDC), and attaches a
|
|
366
366
|
fresh `X-Agent-Token` on every decision. On Azure compute the Managed Identity
|
|
367
|
-
is detected automatically
|
|
367
|
+
is detected automatically - **no GUIDs, authority, or scopes in your code.**
|
|
368
368
|
|
|
369
369
|
```python
|
|
370
370
|
info = shield.agentic.credential_info # ops visibility into the binding
|
|
@@ -402,7 +402,7 @@ app = graph.compile()
|
|
|
402
402
|
## Multimodal guardrails (transparent)
|
|
403
403
|
|
|
404
404
|
Image generation, image edits, audio (TTS / transcription), video, embedding and
|
|
405
|
-
rerank requests are guarded **at the gateway**
|
|
405
|
+
rerank requests are guarded **at the gateway** - no SDK changes and no extra code.
|
|
406
406
|
Keep using the native provider SDKs through DeepIntShield; when the operator
|
|
407
407
|
enables `GUARDRAILS_MULTIMODAL`, the gateway evaluates the text these requests
|
|
408
408
|
already carry (image/TTS/video prompts, transcripts) and the binary artifacts
|
|
@@ -411,7 +411,7 @@ themselves, blocking or flagging per your policies.
|
|
|
411
411
|
```python
|
|
412
412
|
client = shield.openai()
|
|
413
413
|
|
|
414
|
-
# Guarded automatically
|
|
414
|
+
# Guarded automatically - the image prompt is evaluated before generation.
|
|
415
415
|
img = client.images.generate(model="gpt-image-1", prompt="a serene mountain lake")
|
|
416
416
|
|
|
417
417
|
# A blocked prompt surfaces as the provider SDK's normal HTTP error:
|
|
@@ -430,7 +430,7 @@ enforcing (`sync`) or observe-only (`shadow`).
|
|
|
430
430
|
|
|
431
431
|
## MCP
|
|
432
432
|
|
|
433
|
-
Generic MCP support
|
|
433
|
+
Generic MCP support - works with any server connected to your DeepintShield
|
|
434
434
|
gateway. No per-server SDK code; the same `Tool` / `MCPClient` API serves
|
|
435
435
|
DeepWiki, Context7, GitHub MCP, an internal one, and so on.
|
|
436
436
|
|
|
@@ -525,7 +525,7 @@ print(AgentExecutor(agent=agent, tools=mcp_tools).invoke(
|
|
|
525
525
|
)["output"])
|
|
526
526
|
```
|
|
527
527
|
|
|
528
|
-
LangGraph reuses the same `mcp_tools` list
|
|
528
|
+
LangGraph reuses the same `mcp_tools` list - drop them into a `ToolNode`.
|
|
529
529
|
|
|
530
530
|
---
|
|
531
531
|
|
|
@@ -534,16 +534,16 @@ LangGraph reuses the same `mcp_tools` list — drop them into a `ToolNode`.
|
|
|
534
534
|
The SDK automatically participates in the gateway's two cost-reduction layers
|
|
535
535
|
(both controlled by workspace switches under **Cost Optimization**):
|
|
536
536
|
|
|
537
|
-
- **Provider prompt caching**
|
|
537
|
+
- **Provider prompt caching** - every chat client returned by `shield.openai()`,
|
|
538
538
|
`shield.anthropic()`, etc. ships an `httpx` request hook that injects
|
|
539
539
|
Anthropic `cache_control` markers and an OpenAI `prompt_cache_key` so the
|
|
540
540
|
provider reuses KV state for the static prompt prefix. Cached tokens come
|
|
541
541
|
back at the provider's reduced rate (50% off OpenAI, 90% off Anthropic).
|
|
542
|
-
- **Gemini context caching**
|
|
542
|
+
- **Gemini context caching** - opt in with `shield.genai_cached()` (drop-in
|
|
543
543
|
for `shield.genai()`). The wrapper manages the `cachedContents` resource
|
|
544
544
|
lifecycle behind the scenes; the first call with a new static prefix runs
|
|
545
545
|
normally and the next call within the TTL window reuses the cache.
|
|
546
|
-
- **Semantic caching**
|
|
546
|
+
- **Semantic caching** - runs on the gateway; short-circuits requests whose
|
|
547
547
|
embeddings match a previous response within the configured similarity
|
|
548
548
|
threshold. The SDK doesn't need any code change to benefit; results flow
|
|
549
549
|
back through the normal API.
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "deepintshield"
|
|
7
|
-
version = "2.
|
|
8
|
-
description = "Unified Python SDK for routing chat, RAG, agentic tool-gating, identity, and MCP traffic through DeepintShield
|
|
7
|
+
version = "2.5.0"
|
|
8
|
+
description = "Unified Python SDK for routing chat, RAG, agentic tool-gating, identity, and MCP traffic through DeepintShield - drop-in across the top agentic frameworks."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
license = { text = "Apache-2.0" }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Gemini context-cache management for the DeepintShield SDK.
|
|
2
2
|
|
|
3
|
-
Google Gemini doesn't have inline cache markers like Anthropic
|
|
3
|
+
Google Gemini doesn't have inline cache markers like Anthropic - instead it
|
|
4
4
|
exposes an explicit ``cachedContents`` resource with its own lifecycle:
|
|
5
5
|
|
|
6
6
|
1. Client calls ``client.caches.create(...)`` with the static portion of the
|
|
@@ -14,23 +14,23 @@ exposes an explicit ``cachedContents`` resource with its own lifecycle:
|
|
|
14
14
|
This module wraps that lifecycle so customers don't have to manage cache
|
|
15
15
|
resources by hand. Design choices for zero-latency / scalable / parallel use:
|
|
16
16
|
|
|
17
|
-
* **Per-process registry**
|
|
17
|
+
* **Per-process registry** - a thread-safe dict of ``prefix_hash → (name,
|
|
18
18
|
expires_at_ms)``. Lookups are O(1), no I/O.
|
|
19
|
-
* **Fire-and-forget creation**
|
|
19
|
+
* **Fire-and-forget creation** - the first request that sees a prefix doesn't
|
|
20
20
|
wait for the cache resource to exist. It runs through the provider as a
|
|
21
21
|
normal call, while a background thread creates the cache for the *next*
|
|
22
22
|
call. This keeps the first call's latency identical to no-cache.
|
|
23
|
-
* **Lazy TTL eviction**
|
|
23
|
+
* **Lazy TTL eviction** - expired entries are dropped at lookup time. No
|
|
24
24
|
background sweeper, no extra timers.
|
|
25
|
-
* **Workspace switch respected**
|
|
25
|
+
* **Workspace switch respected** - the gateway strips ``cached_content`` from
|
|
26
26
|
outbound requests when the workspace has prompt caching disabled or
|
|
27
27
|
``google`` not in the provider allow-list, so the SDK's injection becomes
|
|
28
28
|
a no-op end-to-end.
|
|
29
|
-
* **Caller-aware**
|
|
29
|
+
* **Caller-aware** - if the caller already passed ``cached_content`` on a
|
|
30
30
|
call's config, we leave it alone.
|
|
31
31
|
|
|
32
32
|
Phase 4 ships an explicit ``shield.gemini_cache_manager()`` plus a wrapped
|
|
33
|
-
``shield.genai_cached()`` client that calls into it automatically
|
|
33
|
+
``shield.genai_cached()`` client that calls into it automatically - opt-in
|
|
34
34
|
because Gemini's storage cost (~$1/M tok/hr on 1.5 Pro) makes blanket
|
|
35
35
|
auto-caching a footgun for prefixes that don't repeat often.
|
|
36
36
|
"""
|
|
@@ -64,7 +64,7 @@ def _ttl_seconds_from_string(ttl: str | int | None) -> int:
|
|
|
64
64
|
"""Parse a workspace-style TTL string ("5m", "1h", "6h", "24h") into seconds.
|
|
65
65
|
|
|
66
66
|
Falls through to ``DEFAULT_TTL_SECONDS`` on unknown values so misconfigured
|
|
67
|
-
workspaces never throw at request time
|
|
67
|
+
workspaces never throw at request time - they just use the default.
|
|
68
68
|
"""
|
|
69
69
|
if ttl is None:
|
|
70
70
|
return DEFAULT_TTL_SECONDS
|
|
@@ -55,7 +55,7 @@ def _mark_anthropic_block_cacheable(block: Any, marker: dict[str, str]) -> bool:
|
|
|
55
55
|
if not isinstance(block, dict):
|
|
56
56
|
return False
|
|
57
57
|
if "cache_control" in block:
|
|
58
|
-
# Caller already marked this block
|
|
58
|
+
# Caller already marked this block - respect it.
|
|
59
59
|
return False
|
|
60
60
|
block["cache_control"] = marker
|
|
61
61
|
return True
|
|
@@ -76,7 +76,7 @@ def _inject_anthropic(
|
|
|
76
76
|
if "system" in requested:
|
|
77
77
|
system = body.get("system")
|
|
78
78
|
if isinstance(system, list) and system:
|
|
79
|
-
# System is already a list of blocks
|
|
79
|
+
# System is already a list of blocks - mark the last one.
|
|
80
80
|
_mark_anthropic_block_cacheable(system[-1], marker)
|
|
81
81
|
elif isinstance(system, str) and system.strip():
|
|
82
82
|
# Promote string-form system prompts into block form so we can attach
|
|
@@ -115,7 +115,7 @@ def _compute_prefix_hash(
|
|
|
115
115
|
) -> str:
|
|
116
116
|
"""Stable 16-char SHA-256 prefix of (system messages + tool defs).
|
|
117
117
|
|
|
118
|
-
OpenAI uses this as a cache partition key
|
|
118
|
+
OpenAI uses this as a cache partition key - same hash → same cache bucket.
|
|
119
119
|
Identical prefixes from different conversations therefore share cache
|
|
120
120
|
entries. Returns "" when there's no meaningful static prefix to hash.
|
|
121
121
|
"""
|
|
@@ -200,7 +200,7 @@ def build_request_hook(
|
|
|
200
200
|
encoded = json.dumps(body, separators=(",", ":")).encode("utf-8")
|
|
201
201
|
request.headers["content-length"] = str(len(encoded))
|
|
202
202
|
# httpx routes the actual transmitted bytes through `request.stream`
|
|
203
|
-
#
|
|
203
|
+
# - a single-shot byte iterator built when the Request was first
|
|
204
204
|
# constructed. Updating only `_content` leaves the stream pointing
|
|
205
205
|
# at the original bytes, so on the wire httpx emits the *old* body
|
|
206
206
|
# while advertising the *new* content-length. The result on the
|
|
@@ -211,7 +211,7 @@ def build_request_hook(
|
|
|
211
211
|
request._content = encoded # type: ignore[attr-defined]
|
|
212
212
|
try:
|
|
213
213
|
from httpx._content import ByteStream # type: ignore
|
|
214
|
-
except ImportError: # httpx <0.24 fallback
|
|
214
|
+
except ImportError: # httpx <0.24 fallback - module path drifted.
|
|
215
215
|
from httpx import _content # type: ignore
|
|
216
216
|
ByteStream = getattr(_content, "ByteStream", None)
|
|
217
217
|
if ByteStream is not None:
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The tool-enforcement half of the SDK: ``decide()`` runs before a gated tool
|
|
4
4
|
executes and the verdict (ALLOW / DENY / MASK / REQUIRE_APPROVAL) maps to a
|
|
5
5
|
return value or an exception. All Entra / identity / policy machinery is
|
|
6
|
-
auto-discovered from the gateway
|
|
6
|
+
auto-discovered from the gateway - the user passes only ``virtual_key`` and
|
|
7
7
|
``base_url`` to :class:`~deepintshield.client.DeepintShield`.
|
|
8
8
|
|
|
9
9
|
Typically consumed via ``shield.agentic`` rather than imported directly::
|
{deepintshield-2.3.0 → deepintshield-2.5.0}/src/deepintshield/agentic/credentials/__init__.py
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
A credential is anything that can produce an agent identity token to present
|
|
4
4
|
alongside the platform VK on every PEP request. The right implementation is
|
|
5
5
|
auto-selected by :class:`AgenticEngine` based on the ``provider_type`` it
|
|
6
|
-
discovers from the gateway
|
|
6
|
+
discovers from the gateway - developers almost never instantiate these
|
|
7
7
|
directly.
|
|
8
8
|
"""
|
|
9
9
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""AgentCredential
|
|
1
|
+
"""AgentCredential - the protocol every identity provider implementation
|
|
2
2
|
follows. The engine only ever calls ``get_token()`` on the abstract type;
|
|
3
3
|
the concrete provider (Entra / ZeroID / OIDC) handles the wire details.
|
|
4
4
|
"""
|
|
@@ -32,7 +32,7 @@ class StaticAgentCredential:
|
|
|
32
32
|
"""Returns a pre-baked token verbatim. For local dev / tests where you
|
|
33
33
|
want a known-good token without running an FIC exchange.
|
|
34
34
|
|
|
35
|
-
Do not use in production
|
|
35
|
+
Do not use in production - the token won't refresh.
|
|
36
36
|
"""
|
|
37
37
|
|
|
38
38
|
def __init__(self, token: str, provider_type: str = "static") -> None:
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
"""Entra Agent ID credential
|
|
1
|
+
"""Entra Agent ID credential - Federated Identity Credential exchange.
|
|
2
2
|
|
|
3
3
|
Flow per call (only when the cached token is missing or near expiry):
|
|
4
4
|
|
|
5
5
|
1. Get a self-token from the local Managed Identity for audience
|
|
6
6
|
``api://AzureADTokenExchange``. Uses DefaultAzureCredential so the MI
|
|
7
7
|
binding is auto-detected from the host (AKS workload identity, App
|
|
8
|
-
Service identity, VM identity, etc.)
|
|
8
|
+
Service identity, VM identity, etc.) - no client_id needs to appear
|
|
9
9
|
in user code.
|
|
10
10
|
|
|
11
11
|
2. POST that as the ``client_assertion`` to the blueprint's token
|
|
@@ -43,7 +43,7 @@ class EntraAgentCredential:
|
|
|
43
43
|
"""Concrete AgentCredential for Microsoft Entra Agent ID blueprints.
|
|
44
44
|
|
|
45
45
|
Constructed via :class:`AgenticEngine` from the values discovered at
|
|
46
|
-
GET /api/agentic-security/vk-credential-info
|
|
46
|
+
GET /api/agentic-security/vk-credential-info - developers should not
|
|
47
47
|
instantiate this directly except for overrides.
|
|
48
48
|
"""
|
|
49
49
|
|
|
@@ -87,7 +87,7 @@ class EntraAgentCredential:
|
|
|
87
87
|
if cached:
|
|
88
88
|
return cached
|
|
89
89
|
with self._cache.lock():
|
|
90
|
-
# Re-check inside lock
|
|
90
|
+
# Re-check inside lock - another thread may have refreshed.
|
|
91
91
|
cached = self._cache.get()
|
|
92
92
|
if cached:
|
|
93
93
|
return cached
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Generic OIDC credential
|
|
1
|
+
"""Generic OIDC credential - Client-Credentials grant against any OIDC IdP
|
|
2
2
|
that publishes a JWKS endpoint (Okta, Auth0, Keycloak, …).
|
|
3
3
|
|
|
4
4
|
Used when the platform VK is bound to a ``generic_oidc`` identity provider on
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"""ZeroID credential
|
|
1
|
+
"""ZeroID credential - RFC 8693 token exchange.
|
|
2
2
|
|
|
3
3
|
ZeroID's wire format follows OAuth 2.0 Token Exchange (RFC 8693) with a
|
|
4
4
|
standard ``act`` claim chain and SPIFFE/WIMSE identity URIs. The gateway's
|
|
5
5
|
broker verifies the resulting JWT against ZeroID's JWKS the same way it does
|
|
6
|
-
for Entra
|
|
6
|
+
for Entra - the SDK side is just the exchange dance.
|
|
7
7
|
|
|
8
8
|
For a full integration you'll typically swap in the vendor SDK's own token
|
|
9
9
|
provider here; this implementation is the minimal HTTP shape that works for
|