@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,199 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
openai_1b_multiuser_bind_streaming.py - Multi-user OpenAI with streaming
|
|
4
|
+
|
|
5
|
+
Same as 1b but demonstrates streaming responses with bind_to_user pattern.
|
|
6
|
+
|
|
7
|
+
Prerequisites:
|
|
8
|
+
- MACAW SDK installed (pip install macaw-client macaw-adapters)
|
|
9
|
+
- OPENAI_API_KEY environment variable
|
|
10
|
+
- Identity Provider configured (Console -> Settings -> Identity Bridge)
|
|
11
|
+
- Test users: alice/Alice123!, bob/Bob@123!
|
|
12
|
+
|
|
13
|
+
Run:
|
|
14
|
+
export OPENAI_API_KEY=sk-...
|
|
15
|
+
python openai_1b_multiuser_bind_streaming.py
|
|
16
|
+
|
|
17
|
+
No IdP configured? Run simpler example first:
|
|
18
|
+
python openai_1a_dropin_simple.py
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
|
|
24
|
+
from macaw_adapters.openai import SecureOpenAI
|
|
25
|
+
from macaw_client import MACAWClient, RemoteIdentityProvider
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Test configurations based on user policies
|
|
29
|
+
# Alice: GPT-3.5 only, max 500 tokens
|
|
30
|
+
# Bob: GPT-3.5/4, max 2000 tokens
|
|
31
|
+
USER_TESTS = {
|
|
32
|
+
"alice": {
|
|
33
|
+
"password": "Alice123!",
|
|
34
|
+
"policy_desc": "GPT-3.5 only, max 500 tokens",
|
|
35
|
+
"tests": [
|
|
36
|
+
# (model, max_tokens, should_succeed)
|
|
37
|
+
("gpt-3.5-turbo", 400, True), # ALLOWED - correct model, within limit
|
|
38
|
+
("gpt-4", 400, False), # BLOCKED - alice can't use GPT-4
|
|
39
|
+
("gpt-3.5-turbo", 600, False), # BLOCKED - exceeds max_tokens
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"bob": {
|
|
43
|
+
"password": "Bob@123!",
|
|
44
|
+
"policy_desc": "GPT-3.5/4, max 2000 tokens",
|
|
45
|
+
"tests": [
|
|
46
|
+
("gpt-3.5-turbo", 400, True), # ALLOWED
|
|
47
|
+
("gpt-4", 400, True), # ALLOWED - bob CAN use GPT-4
|
|
48
|
+
("gpt-3.5-turbo", 600, True), # ALLOWED - within bob's 2000 limit
|
|
49
|
+
("gpt-4", 2500, False), # BLOCKED - exceeds max_tokens
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def create_user_client(username: str, password: str) -> MACAWClient:
|
|
56
|
+
"""Create authenticated user client."""
|
|
57
|
+
print(f" Authenticating {username}...")
|
|
58
|
+
jwt_token, _ = RemoteIdentityProvider().login(username, password)
|
|
59
|
+
|
|
60
|
+
user = MACAWClient(
|
|
61
|
+
user_name=username,
|
|
62
|
+
iam_token=jwt_token,
|
|
63
|
+
agent_type="user",
|
|
64
|
+
app_name="financial-app"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if not user.register():
|
|
68
|
+
raise RuntimeError(f"Failed to register user {username}")
|
|
69
|
+
|
|
70
|
+
print(f" User agent: {user.agent_id}")
|
|
71
|
+
return user
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_user(username: str, openai_service: SecureOpenAI):
|
|
75
|
+
"""Test a user with bind_to_user pattern."""
|
|
76
|
+
config = USER_TESTS[username]
|
|
77
|
+
|
|
78
|
+
print(f"\n{'=' * 60}")
|
|
79
|
+
print(f"Testing {username.upper()} via bind_to_user")
|
|
80
|
+
print(f"Policy: {config['policy_desc']}")
|
|
81
|
+
print("=" * 60)
|
|
82
|
+
|
|
83
|
+
# 1. Create user client with JWT
|
|
84
|
+
user_client = create_user_client(username, config["password"])
|
|
85
|
+
|
|
86
|
+
# 2. Bind user to service
|
|
87
|
+
print(f"\n Binding to service: {openai_service.server_id}")
|
|
88
|
+
user_openai = openai_service.bind_to_user(user_client)
|
|
89
|
+
|
|
90
|
+
# 3. Test with different parameters
|
|
91
|
+
print(f"\n Running tests:")
|
|
92
|
+
for model, max_tokens, should_succeed in config["tests"]:
|
|
93
|
+
expected = "SHOULD SUCCEED" if should_succeed else "SHOULD BE BLOCKED"
|
|
94
|
+
print(f"\n -> {model}, {max_tokens} tokens ({expected})")
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
response = user_openai.chat.completions.create(
|
|
98
|
+
model=model,
|
|
99
|
+
max_tokens=max_tokens,
|
|
100
|
+
messages=[
|
|
101
|
+
{"role": "system", "content": "You are a financial analyst."},
|
|
102
|
+
{"role": "user", "content": "What is revenue growth? Brief answer."}
|
|
103
|
+
],
|
|
104
|
+
stream=True
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
if should_succeed:
|
|
108
|
+
print(f" SUCCESS (as expected)")
|
|
109
|
+
# Collect streaming chunks
|
|
110
|
+
collected_content = []
|
|
111
|
+
chunk_count = 0
|
|
112
|
+
for chunk in response:
|
|
113
|
+
chunk_count += 1
|
|
114
|
+
if chunk.choices and chunk.choices[0].delta.content:
|
|
115
|
+
collected_content.append(chunk.choices[0].delta.content)
|
|
116
|
+
full_content = "".join(collected_content)
|
|
117
|
+
print(f" Chunks received: {chunk_count}")
|
|
118
|
+
print(f" Response: {full_content[:60]}...")
|
|
119
|
+
else:
|
|
120
|
+
print(f" UNEXPECTED SUCCESS - should have been blocked!")
|
|
121
|
+
|
|
122
|
+
except Exception as e:
|
|
123
|
+
error = str(e)
|
|
124
|
+
is_policy_block = any(word in error.lower() for word in
|
|
125
|
+
["not in allowed", "not permitted", "policy",
|
|
126
|
+
"max_tokens", "model", "blocked", "denied"])
|
|
127
|
+
if is_policy_block:
|
|
128
|
+
if should_succeed:
|
|
129
|
+
print(f" UNEXPECTED BLOCK: {error[:60]}...")
|
|
130
|
+
else:
|
|
131
|
+
print(f" CORRECTLY BLOCKED by policy")
|
|
132
|
+
else:
|
|
133
|
+
print(f" ERROR: {error[:60]}...")
|
|
134
|
+
|
|
135
|
+
# 4. Cleanup
|
|
136
|
+
print(f"\n Unbinding user...")
|
|
137
|
+
user_openai.unbind()
|
|
138
|
+
print(f" is_bound: {user_openai.is_bound}")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def main():
|
|
142
|
+
# Check for API key
|
|
143
|
+
if not os.environ.get("OPENAI_API_KEY"):
|
|
144
|
+
print("Set OPENAI_API_KEY environment variable")
|
|
145
|
+
print(" export OPENAI_API_KEY=sk-...")
|
|
146
|
+
return
|
|
147
|
+
|
|
148
|
+
print("=" * 60)
|
|
149
|
+
print("Example 1b: Multi-user with bind_to_user (OpenAI)")
|
|
150
|
+
print("=" * 60)
|
|
151
|
+
print("\nPath: bind_to_user (per-user identity)")
|
|
152
|
+
print("Use when: SaaS app, different users need different permissions")
|
|
153
|
+
print("\nArchitecture:")
|
|
154
|
+
print(" - Single SecureOpenAI service (shared)")
|
|
155
|
+
print(" - Per-user MACAWClient with JWT identity")
|
|
156
|
+
print(" - bind_to_user() connects user to service")
|
|
157
|
+
print(" - User's identity flows through for policy evaluation")
|
|
158
|
+
|
|
159
|
+
# Create SINGLE service (shared across all users)
|
|
160
|
+
print("\n--- Creating SecureOpenAI service ---")
|
|
161
|
+
openai_service = SecureOpenAI(app_name="openai-service")
|
|
162
|
+
print(f"Service registered: {openai_service.server_id}")
|
|
163
|
+
print(f"Mode: {openai_service._mode}")
|
|
164
|
+
|
|
165
|
+
# Test alice (restricted)
|
|
166
|
+
try:
|
|
167
|
+
test_user("alice", openai_service)
|
|
168
|
+
except Exception as e:
|
|
169
|
+
print(f"\nFailed to test alice: {e}")
|
|
170
|
+
|
|
171
|
+
# Test bob (enhanced)
|
|
172
|
+
try:
|
|
173
|
+
test_user("bob", openai_service)
|
|
174
|
+
except Exception as e:
|
|
175
|
+
print(f"\nFailed to test bob: {e}")
|
|
176
|
+
|
|
177
|
+
print("\n" + "=" * 60)
|
|
178
|
+
print("Example complete!")
|
|
179
|
+
print("=" * 60)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
if __name__ == "__main__":
|
|
183
|
+
try:
|
|
184
|
+
main()
|
|
185
|
+
except Exception as e:
|
|
186
|
+
err = str(e)
|
|
187
|
+
print("\n" + "=" * 60)
|
|
188
|
+
if "OPENAI_API_KEY" in err or "api_key" in err.lower():
|
|
189
|
+
print("ERROR: OpenAI API key not configured")
|
|
190
|
+
print("Fix: export OPENAI_API_KEY=sk-...")
|
|
191
|
+
elif "Local provider does not support" in err:
|
|
192
|
+
print("ERROR: Identity Provider not configured")
|
|
193
|
+
print("Fix: Console -> Settings -> Identity Bridge")
|
|
194
|
+
print("\nOr run simpler example first:")
|
|
195
|
+
print(" python openai_1a_dropin_simple.py")
|
|
196
|
+
else:
|
|
197
|
+
print(f"ERROR: {e}")
|
|
198
|
+
print("=" * 60)
|
|
199
|
+
sys.exit(1)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
openai_1c_a2a_invoke.py - Agent-to-Agent invocation with invoke_tool
|
|
4
|
+
|
|
5
|
+
Demonstrates explicit tool invocation for agent systems and cross-service calls.
|
|
6
|
+
Use when you need fine-grained control over routing and tool invocation.
|
|
7
|
+
|
|
8
|
+
Prerequisites:
|
|
9
|
+
- MACAW SDK installed (pip install macaw-client macaw-adapters)
|
|
10
|
+
- OPENAI_API_KEY environment variable
|
|
11
|
+
- Identity Provider configured (Console -> Settings -> Identity Bridge)
|
|
12
|
+
- Test users: alice/Alice123!
|
|
13
|
+
|
|
14
|
+
Run:
|
|
15
|
+
export OPENAI_API_KEY=sk-...
|
|
16
|
+
python openai_1c_a2a_invoke.py
|
|
17
|
+
|
|
18
|
+
No IdP configured? Run simpler example first:
|
|
19
|
+
python openai_1a_dropin_simple.py
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import os
|
|
23
|
+
import sys
|
|
24
|
+
|
|
25
|
+
from macaw_adapters.openai import SecureOpenAI
|
|
26
|
+
from macaw_client import MACAWClient, RemoteIdentityProvider
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main():
|
|
30
|
+
# Check for API key
|
|
31
|
+
if not os.environ.get("OPENAI_API_KEY"):
|
|
32
|
+
print("Set OPENAI_API_KEY environment variable")
|
|
33
|
+
print(" export OPENAI_API_KEY=sk-...")
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
print("=" * 60)
|
|
37
|
+
print("Example 1c: A2A with invoke_tool (OpenAI)")
|
|
38
|
+
print("=" * 60)
|
|
39
|
+
print("\nPath: invoke_tool (explicit A2A control)")
|
|
40
|
+
print("Use when: Agent systems, explicit control, cross-service calls")
|
|
41
|
+
print("\nArchitecture:")
|
|
42
|
+
print(" - SecureOpenAI service registers tools")
|
|
43
|
+
print(" - User agent calls invoke_tool() directly")
|
|
44
|
+
print(" - Must know MAPL tool name: tool:<app>/generate")
|
|
45
|
+
print(" - Returns raw dict (not SDK types)")
|
|
46
|
+
|
|
47
|
+
# 1. Create OpenAI service
|
|
48
|
+
print("\n--- Creating SecureOpenAI service ---")
|
|
49
|
+
openai_service = SecureOpenAI(app_name="openai-service")
|
|
50
|
+
print(f"Service registered: {openai_service.server_id}")
|
|
51
|
+
print(f"Tools registered:")
|
|
52
|
+
for tool_name in openai_service.tools.keys():
|
|
53
|
+
print(f" - {tool_name}")
|
|
54
|
+
|
|
55
|
+
# 2. Create user agent with JWT
|
|
56
|
+
print("\n--- Creating user agent ---")
|
|
57
|
+
print(" Authenticating alice...")
|
|
58
|
+
jwt_token, _ = RemoteIdentityProvider().login("alice", "Alice123!")
|
|
59
|
+
|
|
60
|
+
user = MACAWClient(
|
|
61
|
+
user_name="alice",
|
|
62
|
+
iam_token=jwt_token,
|
|
63
|
+
agent_type="user",
|
|
64
|
+
app_name="my-agent"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if not user.register():
|
|
68
|
+
print(" Failed to register user agent")
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
print(f" User agent: {user.agent_id}")
|
|
72
|
+
|
|
73
|
+
# 3. Explicit invoke_tool
|
|
74
|
+
print("\n--- Test 1: Direct invoke_tool ---")
|
|
75
|
+
print(f" Tool name: tool:{openai_service.app_name}/generate")
|
|
76
|
+
print(f" Target: {openai_service.server_id}")
|
|
77
|
+
|
|
78
|
+
result = user.invoke_tool(
|
|
79
|
+
tool_name=f"tool:{openai_service.app_name}/generate",
|
|
80
|
+
parameters={
|
|
81
|
+
"model": "gpt-3.5-turbo",
|
|
82
|
+
"max_tokens": 100,
|
|
83
|
+
"messages": [
|
|
84
|
+
{"role": "system", "content": "You are a financial analyst."},
|
|
85
|
+
{"role": "user", "content": "What is compound interest? Brief answer."}
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
target_agent=openai_service.server_id
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Result is raw dict - you handle parsing
|
|
92
|
+
if isinstance(result, dict):
|
|
93
|
+
if "error" in result:
|
|
94
|
+
print(f" Error: {result['error']}")
|
|
95
|
+
elif "choices" in result:
|
|
96
|
+
content = result["choices"][0]["message"]["content"]
|
|
97
|
+
print(f" Success!")
|
|
98
|
+
print(f" Model: {result.get('model')}")
|
|
99
|
+
print(f" Response: {content[:80]}...")
|
|
100
|
+
else:
|
|
101
|
+
print(f" Unexpected result: {result}")
|
|
102
|
+
|
|
103
|
+
# 4. Test policy enforcement
|
|
104
|
+
print("\n--- Test 2: Policy enforcement (GPT-4 blocked for alice) ---")
|
|
105
|
+
try:
|
|
106
|
+
result = user.invoke_tool(
|
|
107
|
+
tool_name=f"tool:{openai_service.app_name}/generate",
|
|
108
|
+
parameters={
|
|
109
|
+
"model": "gpt-4", # Alice can't use GPT-4!
|
|
110
|
+
"max_tokens": 100,
|
|
111
|
+
"messages": [{"role": "user", "content": "Hello"}]
|
|
112
|
+
},
|
|
113
|
+
target_agent=openai_service.server_id
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if isinstance(result, dict) and "error" in result:
|
|
117
|
+
print(f" Correctly blocked: {result['error'][:60]}...")
|
|
118
|
+
else:
|
|
119
|
+
print(f" Unexpected - should have been blocked!")
|
|
120
|
+
except Exception as e:
|
|
121
|
+
print(f" Correctly blocked: {str(e)[:60]}...")
|
|
122
|
+
|
|
123
|
+
# 5. Service discovery (optional advanced feature)
|
|
124
|
+
print("\n--- Service Discovery ---")
|
|
125
|
+
print(" (Agents can discover services dynamically via registry)")
|
|
126
|
+
print(f" Known service: {openai_service.server_id}")
|
|
127
|
+
print(f" Tool pattern: tool:<app_name>/generate")
|
|
128
|
+
|
|
129
|
+
print("\n" + "=" * 60)
|
|
130
|
+
print("Example complete!")
|
|
131
|
+
print("=" * 60)
|
|
132
|
+
print("\nKey differences from bind_to_user:")
|
|
133
|
+
print(" - Must know MAPL tool names (tool:xxx/generate)")
|
|
134
|
+
print(" - Must know target agent server_id")
|
|
135
|
+
print(" - Returns raw dict (not ChatCompletion)")
|
|
136
|
+
print(" - More control, but more responsibility")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
if __name__ == "__main__":
|
|
140
|
+
try:
|
|
141
|
+
main()
|
|
142
|
+
except Exception as e:
|
|
143
|
+
err = str(e)
|
|
144
|
+
print("\n" + "=" * 60)
|
|
145
|
+
if "OPENAI_API_KEY" in err or "api_key" in err.lower():
|
|
146
|
+
print("ERROR: OpenAI API key not configured")
|
|
147
|
+
print("Fix: export OPENAI_API_KEY=sk-...")
|
|
148
|
+
elif "Local provider does not support" in err:
|
|
149
|
+
print("ERROR: Identity Provider not configured")
|
|
150
|
+
print("Fix: Console -> Settings -> Identity Bridge")
|
|
151
|
+
print("\nOr run simpler example first:")
|
|
152
|
+
print(" python openai_1a_dropin_simple.py")
|
|
153
|
+
else:
|
|
154
|
+
print(f"ERROR: {e}")
|
|
155
|
+
print("=" * 60)
|
|
156
|
+
sys.exit(1)
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
openai_1d_invocation_patterns.py - Compare invoke_tool vs bind_to_user
|
|
4
|
+
|
|
5
|
+
Compares two ways to invoke SecureOpenAI with the same security guarantees:
|
|
6
|
+
|
|
7
|
+
PATH 1: Direct invoke_tool
|
|
8
|
+
- Explicit A2A (agent-to-agent) control
|
|
9
|
+
- Must know MAPL tool name: tool:<app>/generate
|
|
10
|
+
- Returns raw dict
|
|
11
|
+
- Use when: Agent systems, cross-service calls, custom routing
|
|
12
|
+
|
|
13
|
+
PATH 2: bind_to_user wrapper
|
|
14
|
+
- OpenAI-compatible API (chat.completions.create)
|
|
15
|
+
- Familiar SDK experience
|
|
16
|
+
- Internally calls invoke_tool
|
|
17
|
+
- Use when: Drop-in replacement, developer ergonomics
|
|
18
|
+
|
|
19
|
+
Both paths enforce per-user policies and propagate JWT identity.
|
|
20
|
+
|
|
21
|
+
Prerequisites:
|
|
22
|
+
- MACAW SDK installed (pip install macaw-client macaw-adapters)
|
|
23
|
+
- OPENAI_API_KEY environment variable
|
|
24
|
+
- Identity Provider configured (Console -> Settings -> Identity Bridge)
|
|
25
|
+
- Test users: alice/Alice123!, bob/Bob@123!
|
|
26
|
+
|
|
27
|
+
Run:
|
|
28
|
+
export OPENAI_API_KEY=sk-...
|
|
29
|
+
python openai_1d_invocation_patterns.py
|
|
30
|
+
|
|
31
|
+
No IdP configured? Run simpler example first:
|
|
32
|
+
python openai_1a_dropin_simple.py
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
import os
|
|
36
|
+
import sys
|
|
37
|
+
|
|
38
|
+
from macaw_adapters.openai import SecureOpenAI
|
|
39
|
+
from macaw_client import MACAWClient, RemoteIdentityProvider
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# User-specific test configurations based on their policies
|
|
43
|
+
# Alice: GPT-3.5 only, max 500 tokens
|
|
44
|
+
# Bob: GPT-3.5/4, max 2000 tokens
|
|
45
|
+
USER_TESTS = {
|
|
46
|
+
"alice": {
|
|
47
|
+
"password": "Alice123!",
|
|
48
|
+
"policy_desc": "gpt-3.5-turbo only, max_tokens <= 500",
|
|
49
|
+
"tests": [
|
|
50
|
+
# (model, max_tokens, query, should_succeed)
|
|
51
|
+
("gpt-3.5-turbo", 400, "What is revenue growth?", True), # ALLOWED
|
|
52
|
+
("gpt-4", 400, "What is revenue growth?", False), # BLOCKED - wrong model
|
|
53
|
+
("gpt-3.5-turbo", 600, "What is compound interest?", False), # BLOCKED - exceeds max_tokens
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"bob": {
|
|
57
|
+
"password": "Bob@123!",
|
|
58
|
+
"policy_desc": "gpt-3.5-turbo/gpt-4, max_tokens <= 2000",
|
|
59
|
+
"tests": [
|
|
60
|
+
# (model, max_tokens, query, should_succeed)
|
|
61
|
+
("gpt-3.5-turbo", 400, "What is revenue growth?", True), # ALLOWED
|
|
62
|
+
("gpt-4", 400, "What is market cap?", True), # ALLOWED - Bob CAN use gpt-4
|
|
63
|
+
("gpt-3.5-turbo", 600, "What is compound interest?", True), # ALLOWED - Bob's limit is 2000
|
|
64
|
+
("gpt-4", 2500, "Deep financial analysis", False), # BLOCKED - exceeds max_tokens
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_user_path1(username: str, openai_service: SecureOpenAI):
|
|
71
|
+
"""
|
|
72
|
+
PATH 1: Direct invoke_tool (explicit A2A control).
|
|
73
|
+
|
|
74
|
+
invoke_tool auto-creates authenticated prompts based on registry lookup.
|
|
75
|
+
"""
|
|
76
|
+
print(f"\n{'='*60}")
|
|
77
|
+
print(f"PATH 1: {username.upper()} via invoke_tool")
|
|
78
|
+
print("="*60)
|
|
79
|
+
|
|
80
|
+
user_config = USER_TESTS[username]
|
|
81
|
+
|
|
82
|
+
# 1. Get JWT
|
|
83
|
+
print(f"1. Authenticating...")
|
|
84
|
+
jwt_token, _ = RemoteIdentityProvider().login(username, user_config["password"])
|
|
85
|
+
print(" OK Got JWT token")
|
|
86
|
+
|
|
87
|
+
# 2. Create user agent with JWT
|
|
88
|
+
print("2. Creating user agent...")
|
|
89
|
+
user = MACAWClient(
|
|
90
|
+
user_name=username,
|
|
91
|
+
iam_token=jwt_token, # Converted to embedded_context automatically!
|
|
92
|
+
agent_type="user",
|
|
93
|
+
app_name="financial-analyzer"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
if not user.register():
|
|
97
|
+
print(" FAIL Failed to create user agent")
|
|
98
|
+
return None
|
|
99
|
+
print(f" OK User agent: {user.agent_id}")
|
|
100
|
+
|
|
101
|
+
# 3. Test with invoke_tool (auto-creates authenticated prompts!)
|
|
102
|
+
tests = user_config["tests"]
|
|
103
|
+
|
|
104
|
+
print(f"\n3. Testing via invoke_tool to service {openai_service.server_id}:")
|
|
105
|
+
print(" (invoke_tool will auto-create authenticated prompts for 'messages')")
|
|
106
|
+
print(f" Policy: {username} -> {user_config['policy_desc']}")
|
|
107
|
+
|
|
108
|
+
for model, max_tokens, query, should_succeed in tests:
|
|
109
|
+
expected = "SHOULD SUCCEED" if should_succeed else "SHOULD BE BLOCKED"
|
|
110
|
+
print(f"\n -> {model} with {max_tokens} tokens ({expected})")
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
# A2A call to single service agent
|
|
114
|
+
# invoke_tool auto-creates authenticated prompts!
|
|
115
|
+
result = user.invoke_tool(
|
|
116
|
+
tool_name=f"tool:{openai_service.app_name}/generate",
|
|
117
|
+
parameters={
|
|
118
|
+
"model": model,
|
|
119
|
+
"max_tokens": max_tokens,
|
|
120
|
+
"messages": [
|
|
121
|
+
{"role": "system", "content": "You are a financial analyst."},
|
|
122
|
+
{"role": "user", "content": query}
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
target_agent=openai_service.server_id
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Check if we got a valid response
|
|
129
|
+
if isinstance(result, dict):
|
|
130
|
+
if "error" in result:
|
|
131
|
+
if should_succeed:
|
|
132
|
+
print(f" FAIL UNEXPECTED FAILURE: {result['error']}")
|
|
133
|
+
else:
|
|
134
|
+
print(f" PASS CORRECTLY BLOCKED: {result['error'][:60]}...")
|
|
135
|
+
elif "choices" in result:
|
|
136
|
+
if should_succeed:
|
|
137
|
+
print(f" PASS SUCCESS (as expected)")
|
|
138
|
+
print(f" Model: {result.get('model')}")
|
|
139
|
+
content = result.get('choices', [{}])[0].get('message', {}).get('content', '')
|
|
140
|
+
print(f" Response: {content[:80]}...")
|
|
141
|
+
else:
|
|
142
|
+
print(f" FAIL UNEXPECTED SUCCESS - should have been blocked!")
|
|
143
|
+
else:
|
|
144
|
+
print(f" FAIL Unexpected response: {result}")
|
|
145
|
+
else:
|
|
146
|
+
print(f" FAIL Invalid response type: {type(result)}")
|
|
147
|
+
|
|
148
|
+
except Exception as e:
|
|
149
|
+
error = str(e)
|
|
150
|
+
is_policy_block = ("not in allowed" in error or "not permitted" in error or
|
|
151
|
+
"policy" in error.lower() or "max_tokens" in error or
|
|
152
|
+
"model" in error or "blocked" in error.lower())
|
|
153
|
+
if is_policy_block:
|
|
154
|
+
if should_succeed:
|
|
155
|
+
print(f" FAIL UNEXPECTED BLOCK: {error}")
|
|
156
|
+
else:
|
|
157
|
+
print(f" PASS CORRECTLY BLOCKED by policy")
|
|
158
|
+
else:
|
|
159
|
+
print(f" FAIL Error: {error}")
|
|
160
|
+
|
|
161
|
+
return user
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def test_user_path2(username: str, openai_service: SecureOpenAI):
|
|
165
|
+
"""
|
|
166
|
+
PATH 2: bind_to_user wrapper (OpenAI-compatible API).
|
|
167
|
+
|
|
168
|
+
Same underlying invoke_tool, but wrapped in OpenAI-compatible API.
|
|
169
|
+
"""
|
|
170
|
+
print(f"\n{'='*60}")
|
|
171
|
+
print(f"PATH 2: {username.upper()} via bind_to_user")
|
|
172
|
+
print("="*60)
|
|
173
|
+
|
|
174
|
+
user_config = USER_TESTS[username]
|
|
175
|
+
|
|
176
|
+
# 1. Get JWT
|
|
177
|
+
print(f"1. Authenticating...")
|
|
178
|
+
jwt_token, _ = RemoteIdentityProvider().login(username, user_config["password"])
|
|
179
|
+
print(" OK Got JWT token")
|
|
180
|
+
|
|
181
|
+
# 2. Create user agent with JWT
|
|
182
|
+
print("2. Creating user agent...")
|
|
183
|
+
user = MACAWClient(
|
|
184
|
+
user_name=username,
|
|
185
|
+
iam_token=jwt_token,
|
|
186
|
+
agent_type="user",
|
|
187
|
+
app_name="financial-analyzer"
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if not user.register():
|
|
191
|
+
print(" FAIL Failed to create user agent")
|
|
192
|
+
return
|
|
193
|
+
print(f" OK User agent: {user.agent_id}")
|
|
194
|
+
|
|
195
|
+
# 3. Bind user to service
|
|
196
|
+
print("3. Binding user to SecureOpenAI service...")
|
|
197
|
+
user_openai = openai_service.bind_to_user(user)
|
|
198
|
+
print(f" OK Bound to: {openai_service.server_id}")
|
|
199
|
+
|
|
200
|
+
# 4. Test with OpenAI-style API
|
|
201
|
+
tests = user_config["tests"]
|
|
202
|
+
|
|
203
|
+
print(f"\n4. Testing via bind_to_user wrapper:")
|
|
204
|
+
print(" (internally calls invoke_tool which auto-creates auth prompts)")
|
|
205
|
+
print(f" Policy: {username} -> {user_config['policy_desc']}")
|
|
206
|
+
|
|
207
|
+
for model, max_tokens, query, should_succeed in tests:
|
|
208
|
+
expected = "SHOULD SUCCEED" if should_succeed else "SHOULD BE BLOCKED"
|
|
209
|
+
print(f"\n -> {model} with {max_tokens} tokens ({expected})")
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
response = user_openai.chat.completions.create(
|
|
213
|
+
model=model,
|
|
214
|
+
max_tokens=max_tokens,
|
|
215
|
+
messages=[
|
|
216
|
+
{"role": "system", "content": "You are a financial analyst."},
|
|
217
|
+
{"role": "user", "content": query}
|
|
218
|
+
]
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
if should_succeed:
|
|
222
|
+
print(f" PASS SUCCESS (as expected)")
|
|
223
|
+
print(f" Model: {response.model}")
|
|
224
|
+
print(f" Response: {response.choices[0].message.content[:80]}...")
|
|
225
|
+
else:
|
|
226
|
+
print(f" FAIL UNEXPECTED SUCCESS - should have been blocked!")
|
|
227
|
+
|
|
228
|
+
except Exception as e:
|
|
229
|
+
error = str(e)
|
|
230
|
+
is_policy_block = ("not in allowed" in error or "not permitted" in error or
|
|
231
|
+
"policy" in error.lower() or "max_tokens" in error or
|
|
232
|
+
"model" in error or "blocked" in error.lower())
|
|
233
|
+
if is_policy_block:
|
|
234
|
+
if should_succeed:
|
|
235
|
+
print(f" FAIL UNEXPECTED BLOCK: {error}")
|
|
236
|
+
else:
|
|
237
|
+
print(f" PASS CORRECTLY BLOCKED by policy")
|
|
238
|
+
else:
|
|
239
|
+
print(f" FAIL Error: {error}")
|
|
240
|
+
|
|
241
|
+
# 5. Demonstrate unbind
|
|
242
|
+
print(f"\n5. Testing unbind()...")
|
|
243
|
+
print(f" is_bound before: {user_openai.is_bound}")
|
|
244
|
+
user_openai.unbind()
|
|
245
|
+
print(f" is_bound after: {user_openai.is_bound}")
|
|
246
|
+
try:
|
|
247
|
+
user_openai.chat.completions.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": "test"}])
|
|
248
|
+
print(f" FAIL Call should have failed after unbind!")
|
|
249
|
+
except RuntimeError as e:
|
|
250
|
+
print(f" PASS Correctly rejected: {str(e)[:50]}...")
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def main():
|
|
254
|
+
"""Compare both invocation patterns with multi-user policy enforcement."""
|
|
255
|
+
print("\n" + "="*70)
|
|
256
|
+
print("Invocation Patterns Demo + MACAW Policy Enforcement")
|
|
257
|
+
print("="*70)
|
|
258
|
+
print("\nArchitecture:")
|
|
259
|
+
print(" - Single SecureOpenAI service agent (created once)")
|
|
260
|
+
print(" - User agents with JWT -> embedded_context (automatic)")
|
|
261
|
+
print(" - A2A calls: User -> SecureOpenAI service")
|
|
262
|
+
print(" - Policy enforcement at ToolAgent PEP level")
|
|
263
|
+
print(" - Authenticated prompts auto-created by invoke_tool")
|
|
264
|
+
|
|
265
|
+
print("\nTwo invocation patterns tested:")
|
|
266
|
+
print(" PATH 1: Direct invoke_tool (explicit A2A control)")
|
|
267
|
+
print(" PATH 2: bind_to_user wrapper (OpenAI-compatible API)")
|
|
268
|
+
|
|
269
|
+
print("\nExpected policies (enforced by ToolAgent PEP):")
|
|
270
|
+
print(" - Alice: GPT-3.5 only, max 500 tokens")
|
|
271
|
+
print(" - Bob: GPT-3.5/4, max 2000 tokens")
|
|
272
|
+
|
|
273
|
+
if not os.environ.get("OPENAI_API_KEY"):
|
|
274
|
+
print("\nWARNING: No OPENAI_API_KEY - demo will fail")
|
|
275
|
+
print(" Set with: export OPENAI_API_KEY=sk-...")
|
|
276
|
+
return
|
|
277
|
+
|
|
278
|
+
# Create SINGLE SecureOpenAI service (ONCE!)
|
|
279
|
+
print("\n" + "="*70)
|
|
280
|
+
print("Creating single SecureOpenAI service agent...")
|
|
281
|
+
print("="*70)
|
|
282
|
+
|
|
283
|
+
openai_service = SecureOpenAI(
|
|
284
|
+
api_key=os.environ.get("OPENAI_API_KEY"),
|
|
285
|
+
app_name="openai-service"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
print(f"OK SecureOpenAI service: {openai_service.server_id}")
|
|
289
|
+
|
|
290
|
+
# Test Alice with PATH 1 (invoke_tool)
|
|
291
|
+
try:
|
|
292
|
+
test_user_path1("alice", openai_service)
|
|
293
|
+
except Exception as e:
|
|
294
|
+
print(f"\nFAIL Failed PATH 1 for alice: {e}")
|
|
295
|
+
|
|
296
|
+
# Test Alice with PATH 2 (bind_to_user)
|
|
297
|
+
try:
|
|
298
|
+
test_user_path2("alice", openai_service)
|
|
299
|
+
except Exception as e:
|
|
300
|
+
print(f"\nFAIL Failed PATH 2 for alice: {e}")
|
|
301
|
+
|
|
302
|
+
# Test Bob with PATH 1 (invoke_tool)
|
|
303
|
+
try:
|
|
304
|
+
test_user_path1("bob", openai_service)
|
|
305
|
+
except Exception as e:
|
|
306
|
+
print(f"\nFAIL Failed PATH 1 for bob: {e}")
|
|
307
|
+
|
|
308
|
+
# Test Bob with PATH 2 (bind_to_user)
|
|
309
|
+
try:
|
|
310
|
+
test_user_path2("bob", openai_service)
|
|
311
|
+
except Exception as e:
|
|
312
|
+
print(f"\nFAIL Failed PATH 2 for bob: {e}")
|
|
313
|
+
|
|
314
|
+
# Summary
|
|
315
|
+
print("\n" + "="*70)
|
|
316
|
+
print("Demo complete!")
|
|
317
|
+
print("="*70)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
if __name__ == "__main__":
|
|
321
|
+
try:
|
|
322
|
+
main()
|
|
323
|
+
except Exception as e:
|
|
324
|
+
err = str(e)
|
|
325
|
+
print("\n" + "=" * 60)
|
|
326
|
+
if "OPENAI_API_KEY" in err or "api_key" in err.lower():
|
|
327
|
+
print("ERROR: OpenAI API key not configured")
|
|
328
|
+
print("Fix: export OPENAI_API_KEY=sk-...")
|
|
329
|
+
elif "Local provider does not support" in err:
|
|
330
|
+
print("ERROR: Identity Provider not configured")
|
|
331
|
+
print("Fix: Console -> Settings -> Identity Bridge")
|
|
332
|
+
print("\nOr run simpler example first:")
|
|
333
|
+
print(" python openai_1a_dropin_simple.py")
|
|
334
|
+
else:
|
|
335
|
+
print(f"ERROR: {e}")
|
|
336
|
+
print("=" * 60)
|
|
337
|
+
sys.exit(1)
|