@macaw-ai/client 0.0.0-stage → 0.9.10
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.
- package/LICENSE.md +339 -0
- package/bin/install.js +278 -0
- package/package.json +37 -4
- package/payload/README.md +56 -0
- package/payload/secureAI/LICENSE +190 -0
- package/payload/secureAI/NOTICE +5 -0
- package/payload/secureAI/demos/policy-controls/1_uncontrolled.py +139 -0
- package/payload/secureAI/demos/policy-controls/2_with_macaw.py +139 -0
- package/payload/secureAI/demos/policy-controls/3_with_user_policies.py +162 -0
- package/payload/secureAI/demos/policy-controls/credentials.txt +12 -0
- package/payload/secureAI/demos/policy-controls/malicious_report.txt +21 -0
- package/payload/secureAI/demos/policy-controls/sales_report.txt +20 -0
- package/payload/secureAI/demos/tutorial-1-rbac/README.md +143 -0
- package/payload/secureAI/demos/tutorial-1-rbac/app.py +147 -0
- package/payload/secureAI/demos/tutorial-1-rbac/authprompts_demo.py +306 -0
- package/payload/secureAI/demos/tutorial-1-rbac/config/claims-config.yaml +152 -0
- package/payload/secureAI/demos/tutorial-1-rbac/demo_secureopenai_with_policies.py +150 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/bu_Analytics.json +8 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/company_FinTech_Corp.json +70 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/llm-base.json +69 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/team_Reporting.json +8 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/user_alice.json +32 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/user_bob.json +28 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/user_carol.json +34 -0
- package/payload/secureAI/demos/tutorial-1-rbac/requirements.txt +6 -0
- package/payload/secureAI/demos/tutorial-1-rbac/setup/auth0_complete_setup.sh +392 -0
- package/payload/secureAI/demos/tutorial-1-rbac/setup/keycloak_complete_setup.sh +320 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/alation-remote-proxy.json +155 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/bu.json +22 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/company.json +17 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/user_alice.json +48 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/user_bob.json +83 -0
- package/payload/secureAI/demos/tutorial-2-intent/README.md +213 -0
- package/payload/secureAI/demos/tutorial-2-intent/script.py +188 -0
- package/payload/secureAI/demos/tutorial-2-intent/setup/alation_verifier.py +177 -0
- package/payload/secureAI/demos/tutorial-2-intent/setup/get_alation_user_token.py +180 -0
- package/payload/secureAI/examples/README.md +244 -0
- package/payload/secureAI/examples/anthropic/anthropic_1a_dropin_simple.py +103 -0
- package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind.py +190 -0
- package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind_streaming.py +190 -0
- package/payload/secureAI/examples/anthropic/anthropic_1c_a2a_invoke.py +156 -0
- package/payload/secureAI/examples/attestations/1a_trade_alice.py +215 -0
- package/payload/secureAI/examples/attestations/1a_trade_bob.py +164 -0
- package/payload/secureAI/examples/attestations/1b_grant_admin.py +170 -0
- package/payload/secureAI/examples/attestations/1b_grant_researcher.py +231 -0
- package/payload/secureAI/examples/attestations/README.md +128 -0
- package/payload/secureAI/examples/langchain/langchain_1a_dropin_simple.py +199 -0
- package/payload/secureAI/examples/langchain/langchain_1b_multiuser.py +262 -0
- package/payload/secureAI/examples/langchain/langchain_1c_orchestration.py +332 -0
- package/payload/secureAI/examples/langchain/langchain_1d_llm_openai.py +130 -0
- package/payload/secureAI/examples/langchain/langchain_1e_llm_anthropic.py +130 -0
- package/payload/secureAI/examples/langchain/langchain_1f_memory.py +214 -0
- package/payload/secureAI/examples/litellm/litellm_1a_dropin_simple.py +131 -0
- package/payload/secureAI/examples/mcp/1a_simple_invocation.py +115 -0
- package/payload/secureAI/examples/mcp/1b_discovery_and_resources.py +142 -0
- package/payload/secureAI/examples/mcp/1c_logging_client.py +100 -0
- package/payload/secureAI/examples/mcp/1d_progress_client.py +100 -0
- package/payload/secureAI/examples/mcp/1e_sampling_client.py +150 -0
- package/payload/secureAI/examples/mcp/1e_sampling_server.py +90 -0
- package/payload/secureAI/examples/mcp/1f_elicitation_client.py +127 -0
- package/payload/secureAI/examples/mcp/1f_elicitation_server.py +73 -0
- package/payload/secureAI/examples/mcp/1g_roots_client.py +122 -0
- package/payload/secureAI/examples/mcp/1g_roots_server.py +85 -0
- package/payload/secureAI/examples/mcp/2a_proxy_snowflake.py +212 -0
- package/payload/secureAI/examples/mcp/2b_proxy_databricks.py +299 -0
- package/payload/secureAI/examples/mcp/2c_proxy_stdio_fetch.py +91 -0
- package/payload/secureAI/examples/mcp/2d_proxy_stdio_salesforce.py +119 -0
- package/payload/secureAI/examples/mcp/2e_proxy_multiuser.py +136 -0
- package/payload/secureAI/examples/mcp/2f_proxy_stdio_databricks.py +144 -0
- package/payload/secureAI/examples/mcp/3a_native_mcp_client.py +104 -0
- package/payload/secureAI/examples/mcp/3b_native_mcp_proxy.py +114 -0
- package/payload/secureAI/examples/mcp/3c_native_mcp_sampling.py +180 -0
- package/payload/secureAI/examples/mcp/3d_native_mcp_2026.py +135 -0
- package/payload/secureAI/examples/mcp/securemcp_calculator.py +248 -0
- package/payload/secureAI/examples/mcp/securemcp_server.py +75 -0
- package/payload/secureAI/examples/openai/openai_1a_dropin_simple.py +102 -0
- package/payload/secureAI/examples/openai/openai_1b_multiuser_bind.py +193 -0
- package/payload/secureAI/examples/openai/openai_1b_multiuser_bind_streaming.py +199 -0
- package/payload/secureAI/examples/openai/openai_1c_a2a_invoke.py +156 -0
- package/payload/secureAI/examples/openai/openai_1d_invocation_patterns.py +337 -0
- package/payload/secureAI/examples/phishing-resistance/1a_login_alice.py +180 -0
- package/payload/secureAI/examples/phishing-resistance/1b_trade_alice.py +241 -0
- package/payload/secureAI/examples/phishing-resistance/1b_trade_bob.py +164 -0
- package/payload/secureAI/examples/phishing-resistance/README.md +124 -0
- package/payload/secureAI/examples/phishing-resistance/get_token.py +114 -0
- package/payload/secureAI/examples/pydantic_ai/README.md +88 -0
- package/payload/secureAI/examples/pydantic_ai/policies/app_pydantic-agent.json +10 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1a_dropin_simple.py +90 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1b_model_routing.py +85 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1c_native_tools.py +83 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1d_multiuser_bind.py +104 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1e_mcp_compose.py +92 -0
- package/payload/secureAI/macaw_adapters/__init__.py +55 -0
- package/payload/secureAI/macaw_adapters/anthropic/REFERENCE.md +374 -0
- package/payload/secureAI/macaw_adapters/anthropic/__init__.py +22 -0
- package/payload/secureAI/macaw_adapters/anthropic/secure_anthropic.py +792 -0
- package/payload/secureAI/macaw_adapters/anthropic_ext_tools/__init__.py +25 -0
- package/payload/secureAI/macaw_adapters/anthropic_ext_tools/secure_anthropic.py +840 -0
- package/payload/secureAI/macaw_adapters/langchain/REFERENCE.md +234 -0
- package/payload/secureAI/macaw_adapters/langchain/__init__.py +100 -0
- package/payload/secureAI/macaw_adapters/langchain/_utils.py +83 -0
- package/payload/secureAI/macaw_adapters/langchain/agents.py +296 -0
- package/payload/secureAI/macaw_adapters/langchain/anthropic.py +456 -0
- package/payload/secureAI/macaw_adapters/langchain/callbacks.py +306 -0
- package/payload/secureAI/macaw_adapters/langchain/memory.py +352 -0
- package/payload/secureAI/macaw_adapters/langchain/openai.py +410 -0
- package/payload/secureAI/macaw_adapters/langchain/tools.py +174 -0
- package/payload/secureAI/macaw_adapters/litellm/README.md +216 -0
- package/payload/secureAI/macaw_adapters/litellm/__init__.py +143 -0
- package/payload/secureAI/macaw_adapters/litellm/secure_litellm.py +686 -0
- package/payload/secureAI/macaw_adapters/mcp/REFERENCE.md +460 -0
- package/payload/secureAI/macaw_adapters/mcp/__init__.py +65 -0
- package/payload/secureAI/macaw_adapters/mcp/_endpoint.py +193 -0
- package/payload/secureAI/macaw_adapters/mcp/_endpoint2.py +225 -0
- package/payload/secureAI/macaw_adapters/mcp/_serve.py +25 -0
- package/payload/secureAI/macaw_adapters/mcp/client.py +721 -0
- package/payload/secureAI/macaw_adapters/mcp/decorators.py +228 -0
- package/payload/secureAI/macaw_adapters/mcp/fastmcp_compat.py +353 -0
- package/payload/secureAI/macaw_adapters/mcp/mcp.py +785 -0
- package/payload/secureAI/macaw_adapters/mcp/proxy.py +626 -0
- package/payload/secureAI/macaw_adapters/mcp/server.py +317 -0
- package/payload/secureAI/macaw_adapters/mcp/subscriptions.py +450 -0
- package/payload/secureAI/macaw_adapters/openai/REFERENCE.md +368 -0
- package/payload/secureAI/macaw_adapters/openai/__init__.py +22 -0
- package/payload/secureAI/macaw_adapters/openai/secure_openai.py +833 -0
- package/payload/secureAI/macaw_adapters/openai_ext_tools/REFERENCE.md +368 -0
- package/payload/secureAI/macaw_adapters/openai_ext_tools/__init__.py +25 -0
- package/payload/secureAI/macaw_adapters/openai_ext_tools/secure_openai.py +876 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/__init__.py +36 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/_core.py +257 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/agent.py +218 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/model.py +99 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/toolset.py +86 -0
- package/payload/secureAI/test_harness_quick.py +296 -0
- package/wheels/macaw_adapters-0.9.10.1-py3-none-any.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp312-cp312-macosx_11_0_arm64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp312-cp312-manylinux_2_17_x86_64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp314-cp314-macosx_11_0_arm64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp314-cp314-manylinux_2_17_x86_64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp314-cp314-win_amd64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp39-cp39-macosx_11_0_arm64.whl +0 -0
- package/README.md +0 -3
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
langchain_1d_llm_openai.py - SecureChatOpenAI drop-in replacement
|
|
4
|
+
|
|
5
|
+
Drop-in replacement for langchain_openai.ChatOpenAI with MACAW protection.
|
|
6
|
+
Demonstrates invoke and streaming with audit logging.
|
|
7
|
+
|
|
8
|
+
Use this when: You want MACAW security on LangChain OpenAI calls without
|
|
9
|
+
changing your application code.
|
|
10
|
+
|
|
11
|
+
Prerequisites:
|
|
12
|
+
- MACAW SDK installed (pip install macaw-client macaw-adapters)
|
|
13
|
+
- OPENAI_API_KEY environment variable
|
|
14
|
+
|
|
15
|
+
Run:
|
|
16
|
+
export OPENAI_API_KEY=sk-...
|
|
17
|
+
python langchain_1d_llm_openai.py
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
|
|
22
|
+
# BEFORE: from langchain_openai import ChatOpenAI
|
|
23
|
+
# AFTER:
|
|
24
|
+
from macaw_adapters.langchain.openai import ChatOpenAI, cleanup
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def main():
|
|
28
|
+
# Check for API key
|
|
29
|
+
if not os.environ.get("OPENAI_API_KEY"):
|
|
30
|
+
print("Set OPENAI_API_KEY environment variable")
|
|
31
|
+
print(" export OPENAI_API_KEY=sk-...")
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
print("=" * 60)
|
|
35
|
+
print("Example 1d: SecureChatOpenAI (LangChain)")
|
|
36
|
+
print("=" * 60)
|
|
37
|
+
print("\nPattern: Drop-in replacement for langchain_openai.ChatOpenAI")
|
|
38
|
+
print("Use when: Add MACAW audit logging to LangChain OpenAI calls")
|
|
39
|
+
print("\nImport change:")
|
|
40
|
+
print(" BEFORE: from langchain_openai import ChatOpenAI")
|
|
41
|
+
print(" AFTER: from macaw_adapters.langchain.openai import ChatOpenAI")
|
|
42
|
+
|
|
43
|
+
# Create SecureChatOpenAI - same API as langchain_openai.ChatOpenAI
|
|
44
|
+
print("\n--- Creating SecureChatOpenAI ---")
|
|
45
|
+
llm = ChatOpenAI(
|
|
46
|
+
model="gpt-3.5-turbo",
|
|
47
|
+
temperature=0.7,
|
|
48
|
+
max_tokens=100
|
|
49
|
+
)
|
|
50
|
+
print(f"Model: {llm.model_name}")
|
|
51
|
+
print(f"Temperature: {llm.temperature}")
|
|
52
|
+
|
|
53
|
+
# Test 1: Basic invoke
|
|
54
|
+
print("\n--- Test 1: Basic invoke ---")
|
|
55
|
+
try:
|
|
56
|
+
response = llm.invoke("What is 2 + 2? Answer in one word.")
|
|
57
|
+
print(f"Response: {response.content}")
|
|
58
|
+
print("(This call was audit-logged by MACAW)")
|
|
59
|
+
except Exception as e:
|
|
60
|
+
print(f"Error: {e}")
|
|
61
|
+
|
|
62
|
+
# Test 2: Invoke with message list
|
|
63
|
+
print("\n--- Test 2: Invoke with messages ---")
|
|
64
|
+
try:
|
|
65
|
+
from langchain_core.messages import HumanMessage, SystemMessage
|
|
66
|
+
messages = [
|
|
67
|
+
SystemMessage(content="You are a helpful assistant. Be concise."),
|
|
68
|
+
HumanMessage(content="Name three primary colors.")
|
|
69
|
+
]
|
|
70
|
+
response = llm.invoke(messages)
|
|
71
|
+
print(f"Response: {response.content}")
|
|
72
|
+
except ImportError:
|
|
73
|
+
print("Skipping (langchain_core not installed)")
|
|
74
|
+
except Exception as e:
|
|
75
|
+
print(f"Error: {e}")
|
|
76
|
+
|
|
77
|
+
# Test 3: Streaming
|
|
78
|
+
print("\n--- Test 3: Streaming ---")
|
|
79
|
+
try:
|
|
80
|
+
print("Response: ", end="", flush=True)
|
|
81
|
+
for chunk in llm.stream("Count from 1 to 5, one number per line."):
|
|
82
|
+
if hasattr(chunk, 'content'):
|
|
83
|
+
print(chunk.content, end="", flush=True)
|
|
84
|
+
else:
|
|
85
|
+
print(chunk, end="", flush=True)
|
|
86
|
+
print("\n(Streaming call was audit-logged by MACAW)")
|
|
87
|
+
except Exception as e:
|
|
88
|
+
print(f"\nError: {e}")
|
|
89
|
+
|
|
90
|
+
# Test 4: Batch
|
|
91
|
+
print("\n--- Test 4: Batch ---")
|
|
92
|
+
try:
|
|
93
|
+
prompts = [
|
|
94
|
+
"What color is the sky? One word.",
|
|
95
|
+
"What color is grass? One word."
|
|
96
|
+
]
|
|
97
|
+
responses = llm.batch(prompts)
|
|
98
|
+
for i, resp in enumerate(responses):
|
|
99
|
+
print(f" Q{i+1}: {prompts[i]}")
|
|
100
|
+
print(f" A{i+1}: {resp.content}")
|
|
101
|
+
except Exception as e:
|
|
102
|
+
print(f"Error: {e}")
|
|
103
|
+
|
|
104
|
+
# Cleanup
|
|
105
|
+
print("\n--- Cleanup ---")
|
|
106
|
+
cleanup()
|
|
107
|
+
|
|
108
|
+
print("\n" + "=" * 60)
|
|
109
|
+
print("Example complete!")
|
|
110
|
+
print("=" * 60)
|
|
111
|
+
print("\nKey points:")
|
|
112
|
+
print(" - Same API as langchain_openai.ChatOpenAI")
|
|
113
|
+
print(" - All calls audit-logged through MACAW")
|
|
114
|
+
print(" - Supports invoke, stream, batch, and async variants")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
try:
|
|
119
|
+
main()
|
|
120
|
+
except Exception as e:
|
|
121
|
+
err = str(e)
|
|
122
|
+
print("\n" + "=" * 60)
|
|
123
|
+
if "OPENAI_API_KEY" in err or "api_key" in err.lower():
|
|
124
|
+
print("ERROR: OpenAI API key not configured")
|
|
125
|
+
print("Fix: export OPENAI_API_KEY=sk-...")
|
|
126
|
+
else:
|
|
127
|
+
print(f"ERROR: {e}")
|
|
128
|
+
print("=" * 60)
|
|
129
|
+
import sys
|
|
130
|
+
sys.exit(1)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
langchain_1e_llm_anthropic.py - SecureChatAnthropic drop-in replacement
|
|
4
|
+
|
|
5
|
+
Drop-in replacement for langchain_anthropic.ChatAnthropic with MACAW protection.
|
|
6
|
+
Demonstrates invoke and streaming with audit logging.
|
|
7
|
+
|
|
8
|
+
Use this when: You want MACAW security on LangChain Anthropic calls without
|
|
9
|
+
changing your application code.
|
|
10
|
+
|
|
11
|
+
Prerequisites:
|
|
12
|
+
- MACAW SDK installed (pip install macaw-client macaw-adapters)
|
|
13
|
+
- ANTHROPIC_API_KEY environment variable
|
|
14
|
+
|
|
15
|
+
Run:
|
|
16
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
17
|
+
python langchain_1e_llm_anthropic.py
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
|
|
22
|
+
# BEFORE: from langchain_anthropic import ChatAnthropic
|
|
23
|
+
# AFTER:
|
|
24
|
+
from macaw_adapters.langchain.anthropic import ChatAnthropic, cleanup
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def main():
|
|
28
|
+
# Check for API key
|
|
29
|
+
if not os.environ.get("ANTHROPIC_API_KEY"):
|
|
30
|
+
print("Set ANTHROPIC_API_KEY environment variable")
|
|
31
|
+
print(" export ANTHROPIC_API_KEY=sk-ant-...")
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
print("=" * 60)
|
|
35
|
+
print("Example 1e: SecureChatAnthropic (LangChain)")
|
|
36
|
+
print("=" * 60)
|
|
37
|
+
print("\nPattern: Drop-in replacement for langchain_anthropic.ChatAnthropic")
|
|
38
|
+
print("Use when: Add MACAW audit logging to LangChain Anthropic calls")
|
|
39
|
+
print("\nImport change:")
|
|
40
|
+
print(" BEFORE: from langchain_anthropic import ChatAnthropic")
|
|
41
|
+
print(" AFTER: from macaw_adapters.langchain.anthropic import ChatAnthropic")
|
|
42
|
+
|
|
43
|
+
# Create SecureChatAnthropic - same API as langchain_anthropic.ChatAnthropic
|
|
44
|
+
print("\n--- Creating SecureChatAnthropic ---")
|
|
45
|
+
llm = ChatAnthropic(
|
|
46
|
+
model="claude-3-haiku-20240307", # Fast model for demo
|
|
47
|
+
temperature=0.7,
|
|
48
|
+
max_tokens=100
|
|
49
|
+
)
|
|
50
|
+
print(f"Model: {llm.model_name}")
|
|
51
|
+
print(f"Temperature: {llm.temperature}")
|
|
52
|
+
|
|
53
|
+
# Test 1: Basic invoke
|
|
54
|
+
print("\n--- Test 1: Basic invoke ---")
|
|
55
|
+
try:
|
|
56
|
+
response = llm.invoke("What is 2 + 2? Answer in one word.")
|
|
57
|
+
print(f"Response: {response.content}")
|
|
58
|
+
print("(This call was audit-logged by MACAW)")
|
|
59
|
+
except Exception as e:
|
|
60
|
+
print(f"Error: {e}")
|
|
61
|
+
|
|
62
|
+
# Test 2: Invoke with message list
|
|
63
|
+
print("\n--- Test 2: Invoke with messages ---")
|
|
64
|
+
try:
|
|
65
|
+
from langchain_core.messages import HumanMessage, SystemMessage
|
|
66
|
+
messages = [
|
|
67
|
+
SystemMessage(content="You are a helpful assistant. Be concise."),
|
|
68
|
+
HumanMessage(content="Name three primary colors.")
|
|
69
|
+
]
|
|
70
|
+
response = llm.invoke(messages)
|
|
71
|
+
print(f"Response: {response.content}")
|
|
72
|
+
except ImportError:
|
|
73
|
+
print("Skipping (langchain_core not installed)")
|
|
74
|
+
except Exception as e:
|
|
75
|
+
print(f"Error: {e}")
|
|
76
|
+
|
|
77
|
+
# Test 3: Streaming
|
|
78
|
+
print("\n--- Test 3: Streaming ---")
|
|
79
|
+
try:
|
|
80
|
+
print("Response: ", end="", flush=True)
|
|
81
|
+
for chunk in llm.stream("Count from 1 to 5, one number per line."):
|
|
82
|
+
if hasattr(chunk, 'content'):
|
|
83
|
+
print(chunk.content, end="", flush=True)
|
|
84
|
+
else:
|
|
85
|
+
print(chunk, end="", flush=True)
|
|
86
|
+
print("\n(Streaming call was audit-logged by MACAW)")
|
|
87
|
+
except Exception as e:
|
|
88
|
+
print(f"\nError: {e}")
|
|
89
|
+
|
|
90
|
+
# Test 4: Batch
|
|
91
|
+
print("\n--- Test 4: Batch ---")
|
|
92
|
+
try:
|
|
93
|
+
prompts = [
|
|
94
|
+
"What color is the sky? One word.",
|
|
95
|
+
"What color is grass? One word."
|
|
96
|
+
]
|
|
97
|
+
responses = llm.batch(prompts)
|
|
98
|
+
for i, resp in enumerate(responses):
|
|
99
|
+
print(f" Q{i+1}: {prompts[i]}")
|
|
100
|
+
print(f" A{i+1}: {resp.content}")
|
|
101
|
+
except Exception as e:
|
|
102
|
+
print(f"Error: {e}")
|
|
103
|
+
|
|
104
|
+
# Cleanup
|
|
105
|
+
print("\n--- Cleanup ---")
|
|
106
|
+
cleanup()
|
|
107
|
+
|
|
108
|
+
print("\n" + "=" * 60)
|
|
109
|
+
print("Example complete!")
|
|
110
|
+
print("=" * 60)
|
|
111
|
+
print("\nKey points:")
|
|
112
|
+
print(" - Same API as langchain_anthropic.ChatAnthropic")
|
|
113
|
+
print(" - All calls audit-logged through MACAW")
|
|
114
|
+
print(" - Supports invoke, stream, batch, and async variants")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
try:
|
|
119
|
+
main()
|
|
120
|
+
except Exception as e:
|
|
121
|
+
err = str(e)
|
|
122
|
+
print("\n" + "=" * 60)
|
|
123
|
+
if "ANTHROPIC_API_KEY" in err or "api_key" in err.lower():
|
|
124
|
+
print("ERROR: Anthropic API key not configured")
|
|
125
|
+
print("Fix: export ANTHROPIC_API_KEY=sk-ant-...")
|
|
126
|
+
else:
|
|
127
|
+
print(f"ERROR: {e}")
|
|
128
|
+
print("=" * 60)
|
|
129
|
+
import sys
|
|
130
|
+
sys.exit(1)
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
langchain_1f_memory.py - Secure Memory with MACAW context backing
|
|
4
|
+
|
|
5
|
+
Drop-in replacement for LangChain memory classes with MACAW context backing.
|
|
6
|
+
Demonstrates multi-session isolation where each user gets separate memory.
|
|
7
|
+
|
|
8
|
+
Use this when: You want conversation memory backed by MACAW's secure context
|
|
9
|
+
vault with audit logging and session isolation.
|
|
10
|
+
|
|
11
|
+
Prerequisites:
|
|
12
|
+
- MACAW SDK installed (pip install macaw-client macaw-adapters)
|
|
13
|
+
- OPENAI_API_KEY environment variable (optional, for LLM demo)
|
|
14
|
+
|
|
15
|
+
Run:
|
|
16
|
+
python langchain_1f_memory.py
|
|
17
|
+
# Or with OpenAI demo:
|
|
18
|
+
export OPENAI_API_KEY=sk-...
|
|
19
|
+
python langchain_1f_memory.py
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import os
|
|
23
|
+
|
|
24
|
+
# BEFORE: from langchain.memory import ConversationBufferMemory
|
|
25
|
+
# AFTER:
|
|
26
|
+
from macaw_adapters.langchain.memory import (
|
|
27
|
+
ConversationBufferMemory,
|
|
28
|
+
ConversationBufferWindowMemory,
|
|
29
|
+
cleanup
|
|
30
|
+
)
|
|
31
|
+
from macaw_adapters.langchain.openai import ChatOpenAI
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def demonstrate_basic_memory():
|
|
35
|
+
"""Basic memory usage."""
|
|
36
|
+
print("\n--- Basic ConversationBufferMemory ---")
|
|
37
|
+
|
|
38
|
+
memory = ConversationBufferMemory(session_id="demo-session")
|
|
39
|
+
|
|
40
|
+
# Simulate conversation
|
|
41
|
+
memory.save_context(
|
|
42
|
+
{"input": "Hi, my name is Alice"},
|
|
43
|
+
{"output": "Hello Alice! How can I help you today?"}
|
|
44
|
+
)
|
|
45
|
+
memory.save_context(
|
|
46
|
+
{"input": "What's the weather like?"},
|
|
47
|
+
{"output": "I don't have real-time weather data, but I can help you find it."}
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Load memory
|
|
51
|
+
history = memory.load_memory_variables({})
|
|
52
|
+
print(f"Conversation history:\n{history['history']}")
|
|
53
|
+
print("\n(All memory operations audit-logged by MACAW)")
|
|
54
|
+
|
|
55
|
+
return memory
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def demonstrate_multi_session():
|
|
59
|
+
"""Multi-session isolation - each user gets separate memory."""
|
|
60
|
+
print("\n--- Multi-Session Isolation ---")
|
|
61
|
+
print("Each user has isolated memory (different session_id)")
|
|
62
|
+
|
|
63
|
+
# Alice's session
|
|
64
|
+
alice_memory = ConversationBufferMemory(session_id="alice-session")
|
|
65
|
+
alice_memory.save_context(
|
|
66
|
+
{"input": "I want to order pizza"},
|
|
67
|
+
{"output": "What toppings would you like?"}
|
|
68
|
+
)
|
|
69
|
+
alice_memory.save_context(
|
|
70
|
+
{"input": "Pepperoni please"},
|
|
71
|
+
{"output": "One pepperoni pizza coming up!"}
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Bob's session (completely isolated)
|
|
75
|
+
bob_memory = ConversationBufferMemory(session_id="bob-session")
|
|
76
|
+
bob_memory.save_context(
|
|
77
|
+
{"input": "I need help with my order"},
|
|
78
|
+
{"output": "Of course! What's your order number?"}
|
|
79
|
+
)
|
|
80
|
+
bob_memory.save_context(
|
|
81
|
+
{"input": "Order #12345"},
|
|
82
|
+
{"output": "I found your order. How can I help?"}
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Show isolation
|
|
86
|
+
print("\nAlice's memory:")
|
|
87
|
+
alice_hist = alice_memory.load_memory_variables({})
|
|
88
|
+
print(f" {alice_hist['history']}")
|
|
89
|
+
|
|
90
|
+
print("\nBob's memory:")
|
|
91
|
+
bob_hist = bob_memory.load_memory_variables({})
|
|
92
|
+
print(f" {bob_hist['history']}")
|
|
93
|
+
|
|
94
|
+
print("\n(Sessions are isolated - Bob can't see Alice's conversation)")
|
|
95
|
+
|
|
96
|
+
return alice_memory, bob_memory
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def demonstrate_window_memory():
|
|
100
|
+
"""Window memory - only keeps last k turns."""
|
|
101
|
+
print("\n--- ConversationBufferWindowMemory (k=2) ---")
|
|
102
|
+
print("Only keeps last 2 conversation turns")
|
|
103
|
+
|
|
104
|
+
memory = ConversationBufferWindowMemory(k=2, session_id="window-demo")
|
|
105
|
+
|
|
106
|
+
# Add 4 turns
|
|
107
|
+
conversations = [
|
|
108
|
+
("Turn 1: Hello", "Hi there!"),
|
|
109
|
+
("Turn 2: How are you?", "I'm doing well!"),
|
|
110
|
+
("Turn 3: What's your name?", "I'm an AI assistant."),
|
|
111
|
+
("Turn 4: Can you help me?", "Of course! What do you need?"),
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
for user_msg, ai_msg in conversations:
|
|
115
|
+
memory.save_context({"input": user_msg}, {"output": ai_msg})
|
|
116
|
+
|
|
117
|
+
# Only last 2 turns should be visible
|
|
118
|
+
history = memory.load_memory_variables({})
|
|
119
|
+
print(f"Window memory (last 2 turns only):\n{history['history']}")
|
|
120
|
+
|
|
121
|
+
return memory
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def demonstrate_with_llm():
|
|
125
|
+
"""Use memory with actual LLM (if API key available)."""
|
|
126
|
+
if not os.environ.get("OPENAI_API_KEY"):
|
|
127
|
+
print("\n--- Skipping LLM demo (no OPENAI_API_KEY) ---")
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
print("\n--- Memory with SecureChatOpenAI ---")
|
|
131
|
+
|
|
132
|
+
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7, max_tokens=50)
|
|
133
|
+
memory = ConversationBufferMemory(session_id="llm-demo")
|
|
134
|
+
|
|
135
|
+
# First turn
|
|
136
|
+
user_input = "My favorite color is blue."
|
|
137
|
+
print(f"User: {user_input}")
|
|
138
|
+
|
|
139
|
+
response = llm.invoke(f"The user said: {user_input}. Acknowledge this.")
|
|
140
|
+
ai_output = response.content
|
|
141
|
+
print(f"AI: {ai_output}")
|
|
142
|
+
|
|
143
|
+
memory.save_context({"input": user_input}, {"output": ai_output})
|
|
144
|
+
|
|
145
|
+
# Second turn - AI should remember
|
|
146
|
+
user_input2 = "What did I just tell you?"
|
|
147
|
+
print(f"\nUser: {user_input2}")
|
|
148
|
+
|
|
149
|
+
history = memory.load_memory_variables({})
|
|
150
|
+
context = f"Previous conversation:\n{history['history']}\n\nUser: {user_input2}"
|
|
151
|
+
|
|
152
|
+
response2 = llm.invoke(context)
|
|
153
|
+
print(f"AI: {response2.content}")
|
|
154
|
+
|
|
155
|
+
memory.save_context({"input": user_input2}, {"output": response2.content})
|
|
156
|
+
|
|
157
|
+
print("\n(Memory backed by MACAW context vault)")
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def main():
|
|
161
|
+
print("=" * 60)
|
|
162
|
+
print("Example 1f: Secure Memory (LangChain)")
|
|
163
|
+
print("=" * 60)
|
|
164
|
+
print("\nPattern: Drop-in replacement for langchain.memory classes")
|
|
165
|
+
print("Use when: Need conversation memory with MACAW context backing")
|
|
166
|
+
print("\nImport change:")
|
|
167
|
+
print(" BEFORE: from langchain.memory import ConversationBufferMemory")
|
|
168
|
+
print(" AFTER: from macaw_adapters.langchain.memory import ConversationBufferMemory")
|
|
169
|
+
|
|
170
|
+
# Demo 1: Basic memory
|
|
171
|
+
basic_memory = demonstrate_basic_memory()
|
|
172
|
+
|
|
173
|
+
# Demo 2: Multi-session isolation
|
|
174
|
+
alice_memory, bob_memory = demonstrate_multi_session()
|
|
175
|
+
|
|
176
|
+
# Demo 3: Window memory
|
|
177
|
+
window_memory = demonstrate_window_memory()
|
|
178
|
+
|
|
179
|
+
# Demo 4: With actual LLM
|
|
180
|
+
demonstrate_with_llm()
|
|
181
|
+
|
|
182
|
+
# Cleanup
|
|
183
|
+
print("\n--- Cleanup ---")
|
|
184
|
+
basic_memory.clear()
|
|
185
|
+
alice_memory.clear()
|
|
186
|
+
bob_memory.clear()
|
|
187
|
+
window_memory.clear()
|
|
188
|
+
cleanup()
|
|
189
|
+
|
|
190
|
+
print("\n" + "=" * 60)
|
|
191
|
+
print("Example complete!")
|
|
192
|
+
print("=" * 60)
|
|
193
|
+
print("\nKey points:")
|
|
194
|
+
print(" - Same API as langchain.memory classes")
|
|
195
|
+
print(" - Memory backed by MACAW context vault")
|
|
196
|
+
print(" - All operations audit-logged")
|
|
197
|
+
print(" - Session isolation via session_id")
|
|
198
|
+
print(" - Supports: ConversationBufferMemory, WindowMemory, SummaryMemory")
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if __name__ == "__main__":
|
|
202
|
+
try:
|
|
203
|
+
main()
|
|
204
|
+
except Exception as e:
|
|
205
|
+
err = str(e)
|
|
206
|
+
print("\n" + "=" * 60)
|
|
207
|
+
if "OPENAI_API_KEY" in err or "api_key" in err.lower():
|
|
208
|
+
print("ERROR: OpenAI API key not configured")
|
|
209
|
+
print("Fix: export OPENAI_API_KEY=sk-...")
|
|
210
|
+
else:
|
|
211
|
+
print(f"ERROR: {e}")
|
|
212
|
+
print("=" * 60)
|
|
213
|
+
import sys
|
|
214
|
+
sys.exit(1)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
litellm_1a_dropin_simple.py - Drop-in replacement for LiteLLM
|
|
4
|
+
|
|
5
|
+
The simplest integration path - just replace your import and it works.
|
|
6
|
+
No Identity Provider needed. Tests multiple providers through unified API.
|
|
7
|
+
|
|
8
|
+
Prerequisites:
|
|
9
|
+
- MACAW SDK installed (pip install macaw-client macaw-adapters[litellm])
|
|
10
|
+
- OPENAI_API_KEY and/or ANTHROPIC_API_KEY environment variables
|
|
11
|
+
|
|
12
|
+
Run:
|
|
13
|
+
export OPENAI_API_KEY=sk-...
|
|
14
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
15
|
+
python litellm_1a_dropin_simple.py
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
|
|
21
|
+
# BEFORE: import litellm
|
|
22
|
+
# AFTER:
|
|
23
|
+
from macaw_adapters.litellm import SecureLiteLLM
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def main():
|
|
27
|
+
# Check for at least one API key
|
|
28
|
+
has_openai = bool(os.environ.get("OPENAI_API_KEY"))
|
|
29
|
+
has_anthropic = bool(os.environ.get("ANTHROPIC_API_KEY"))
|
|
30
|
+
|
|
31
|
+
if not has_openai and not has_anthropic:
|
|
32
|
+
print("Set at least one API key:")
|
|
33
|
+
print(" export OPENAI_API_KEY=sk-...")
|
|
34
|
+
print(" export ANTHROPIC_API_KEY=sk-ant-...")
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
print("=" * 60)
|
|
38
|
+
print("Example 1a: Drop-in Replacement (LiteLLM)")
|
|
39
|
+
print("=" * 60)
|
|
40
|
+
print("\nPath: Direct on service (simplest)")
|
|
41
|
+
print("Use when: Single app, multiple providers, app-level policies")
|
|
42
|
+
print(f"\nProviders available: OpenAI={has_openai}, Anthropic={has_anthropic}")
|
|
43
|
+
|
|
44
|
+
# Define allowed models - only cheaper/faster models
|
|
45
|
+
allowed_models = []
|
|
46
|
+
if has_openai:
|
|
47
|
+
allowed_models.append("openai/gpt-3.5-turbo")
|
|
48
|
+
if has_anthropic:
|
|
49
|
+
allowed_models.append("anthropic/claude-3-haiku-20240307")
|
|
50
|
+
|
|
51
|
+
# Create SecureLiteLLM with policy restricting to allowed models
|
|
52
|
+
client = SecureLiteLLM(
|
|
53
|
+
app_name="my-litellm-app",
|
|
54
|
+
intent_policy={
|
|
55
|
+
"constraints": {
|
|
56
|
+
"parameters": {
|
|
57
|
+
"tool:*/generate": {
|
|
58
|
+
"model": allowed_models,
|
|
59
|
+
"max_tokens": {"max": 200}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
print(f"\nService registered: {client.server_id}")
|
|
67
|
+
print(f"Mode: {client._mode}")
|
|
68
|
+
print(f"Policy: Only {allowed_models}, max 200 tokens")
|
|
69
|
+
|
|
70
|
+
# Test 1: OpenAI backend (if available)
|
|
71
|
+
if has_openai:
|
|
72
|
+
print("\n--- Test 1: OpenAI backend (gpt-3.5-turbo) ---")
|
|
73
|
+
response = client.chat.completions.create(
|
|
74
|
+
model="openai/gpt-3.5-turbo",
|
|
75
|
+
max_tokens=100,
|
|
76
|
+
messages=[
|
|
77
|
+
{"role": "user", "content": "What is 2+2? One word answer."}
|
|
78
|
+
]
|
|
79
|
+
)
|
|
80
|
+
print(f"Model: {response.model}")
|
|
81
|
+
print(f"Response: {response.choices[0].message.content}")
|
|
82
|
+
|
|
83
|
+
# Test 2: Anthropic backend (if available)
|
|
84
|
+
if has_anthropic:
|
|
85
|
+
print("\n--- Test 2: Anthropic backend (claude-3-haiku) ---")
|
|
86
|
+
response = client.chat.completions.create(
|
|
87
|
+
model="anthropic/claude-3-haiku-20240307",
|
|
88
|
+
max_tokens=100,
|
|
89
|
+
messages=[
|
|
90
|
+
{"role": "user", "content": "What is 3+3? One word answer."}
|
|
91
|
+
]
|
|
92
|
+
)
|
|
93
|
+
print(f"Model: {response.model}")
|
|
94
|
+
print(f"Response: {response.choices[0].message.content}")
|
|
95
|
+
|
|
96
|
+
# Test 3: Policy enforcement - try a blocked model
|
|
97
|
+
print("\n--- Test 3: Policy enforcement (blocked model) ---")
|
|
98
|
+
blocked_model = "openai/gpt-4" if has_openai else "anthropic/claude-3-5-sonnet-20241022"
|
|
99
|
+
print(f"Trying blocked model: {blocked_model}")
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
response = client.chat.completions.create(
|
|
103
|
+
model=blocked_model,
|
|
104
|
+
max_tokens=100,
|
|
105
|
+
messages=[
|
|
106
|
+
{"role": "user", "content": "Hello"}
|
|
107
|
+
]
|
|
108
|
+
)
|
|
109
|
+
print(f"Unexpected success - should have been blocked!")
|
|
110
|
+
print(f"Response: {response.choices[0].message.content[:50]}...")
|
|
111
|
+
except Exception as e:
|
|
112
|
+
print(f"Correctly blocked: {e}")
|
|
113
|
+
|
|
114
|
+
print("\n" + "=" * 60)
|
|
115
|
+
print("Example complete!")
|
|
116
|
+
print("=" * 60)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
try:
|
|
121
|
+
main()
|
|
122
|
+
except Exception as e:
|
|
123
|
+
err = str(e)
|
|
124
|
+
print("\n" + "=" * 60)
|
|
125
|
+
if "api_key" in err.lower():
|
|
126
|
+
print("ERROR: API key not configured")
|
|
127
|
+
print("Fix: export OPENAI_API_KEY=sk-... or ANTHROPIC_API_KEY=sk-ant-...")
|
|
128
|
+
else:
|
|
129
|
+
print(f"ERROR: {e}")
|
|
130
|
+
print("=" * 60)
|
|
131
|
+
sys.exit(1)
|