swiftapi-python 1.2.1__py3-none-any.whl → 1.3.0__py3-none-any.whl
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.
- swiftapi/__init__.py +75 -96
- swiftapi/__main__.py +2 -0
- swiftapi/anthropic.py +236 -222
- swiftapi/brake/__init__.py +45 -0
- swiftapi/brake/benchmarks.py +111 -0
- swiftapi/brake/broker.py +274 -0
- swiftapi/brake/core.py +420 -0
- swiftapi/brake/install.py +268 -0
- swiftapi/brake/mcp.py +160 -0
- swiftapi/brake/observations.py +61 -0
- swiftapi/brake/qualification.py +116 -0
- swiftapi/brake/remote.py +90 -0
- swiftapi/cli.py +150 -0
- swiftapi/client.py +328 -307
- swiftapi/enforcement.py +281 -280
- swiftapi/openai.py +243 -225
- swiftapi/py.typed +0 -0
- swiftapi/verifier.py +182 -130
- swiftapi_python-1.3.0.dist-info/METADATA +147 -0
- swiftapi_python-1.3.0.dist-info/RECORD +26 -0
- {swiftapi_python-1.2.1.dist-info → swiftapi_python-1.3.0.dist-info}/WHEEL +1 -1
- swiftapi_python-1.3.0.dist-info/entry_points.txt +2 -0
- swiftapi_python-1.2.1.dist-info/METADATA +0 -255
- swiftapi_python-1.2.1.dist-info/RECORD +0 -13
- {swiftapi_python-1.2.1.dist-info → swiftapi_python-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {swiftapi_python-1.2.1.dist-info → swiftapi_python-1.3.0.dist-info}/top_level.txt +0 -0
swiftapi/__init__.py
CHANGED
|
@@ -1,96 +1,75 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
#
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"verify_signature",
|
|
77
|
-
"is_valid",
|
|
78
|
-
"get_public_key",
|
|
79
|
-
# Exceptions
|
|
80
|
-
"SwiftAPIError",
|
|
81
|
-
"AuthenticationError",
|
|
82
|
-
"PolicyViolation",
|
|
83
|
-
"SignatureVerificationError",
|
|
84
|
-
"AttestationExpiredError",
|
|
85
|
-
"AttestationRevokedError",
|
|
86
|
-
"RateLimitError",
|
|
87
|
-
"NetworkError",
|
|
88
|
-
# UX
|
|
89
|
-
"Colors",
|
|
90
|
-
"Symbols",
|
|
91
|
-
"print_approved",
|
|
92
|
-
"print_denied",
|
|
93
|
-
"print_verified",
|
|
94
|
-
"print_error",
|
|
95
|
-
"print_info",
|
|
96
|
-
]
|
|
1
|
+
"""SwiftAPI: verified execution for explicitly mediated capabilities."""
|
|
2
|
+
__version__ = "1.3.0"
|
|
3
|
+
__author__ = "Rayan Pal"
|
|
4
|
+
|
|
5
|
+
# Core exports
|
|
6
|
+
from .client import SwiftAPI
|
|
7
|
+
from .enforcement import Enforcement, enforce
|
|
8
|
+
from .verifier import verify_signature, is_valid, get_public_key
|
|
9
|
+
from .openai import OpenAI
|
|
10
|
+
from .anthropic import Anthropic
|
|
11
|
+
|
|
12
|
+
# Exceptions
|
|
13
|
+
from .exceptions import (
|
|
14
|
+
SwiftAPIError,
|
|
15
|
+
AuthenticationError,
|
|
16
|
+
PolicyViolation,
|
|
17
|
+
SignatureVerificationError,
|
|
18
|
+
AttestationExpiredError,
|
|
19
|
+
AttestationRevokedError,
|
|
20
|
+
RateLimitError,
|
|
21
|
+
NetworkError,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# UX utilities
|
|
25
|
+
from .utils import (
|
|
26
|
+
Colors,
|
|
27
|
+
Symbols,
|
|
28
|
+
print_approved,
|
|
29
|
+
print_denied,
|
|
30
|
+
print_verified,
|
|
31
|
+
print_error,
|
|
32
|
+
print_info,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
# Version
|
|
37
|
+
"__version__",
|
|
38
|
+
# Core
|
|
39
|
+
"SwiftAPI",
|
|
40
|
+
"Enforcement",
|
|
41
|
+
"enforce",
|
|
42
|
+
# Drop-in replacements
|
|
43
|
+
"OpenAI",
|
|
44
|
+
"Anthropic",
|
|
45
|
+
# Verification
|
|
46
|
+
"verify_signature",
|
|
47
|
+
"is_valid",
|
|
48
|
+
"get_public_key",
|
|
49
|
+
# Exceptions
|
|
50
|
+
"SwiftAPIError",
|
|
51
|
+
"AuthenticationError",
|
|
52
|
+
"PolicyViolation",
|
|
53
|
+
"SignatureVerificationError",
|
|
54
|
+
"AttestationExpiredError",
|
|
55
|
+
"AttestationRevokedError",
|
|
56
|
+
"RateLimitError",
|
|
57
|
+
"NetworkError",
|
|
58
|
+
# UX
|
|
59
|
+
"Colors",
|
|
60
|
+
"Symbols",
|
|
61
|
+
"print_approved",
|
|
62
|
+
"print_denied",
|
|
63
|
+
"print_verified",
|
|
64
|
+
"print_error",
|
|
65
|
+
"print_info",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
from .brake import (RemoteAuthority, ActionEnvelope, BindingCondition, ContinuationAuthority, Decision,
|
|
69
|
+
DecisionReceipt, LocalAuthority, LocalGuard, CapabilityBroker,
|
|
70
|
+
AuthorizationDenied, ReplayError, verify_receipt)
|
|
71
|
+
__all__ += ["ActionEnvelope", "BindingCondition", "ContinuationAuthority", "Decision",
|
|
72
|
+
"DecisionReceipt", "LocalAuthority", "LocalGuard", "CapabilityBroker",
|
|
73
|
+
"AuthorizationDenied", "ReplayError", "verify_receipt"]
|
|
74
|
+
|
|
75
|
+
__all__ += ["RemoteAuthority"]
|
swiftapi/__main__.py
ADDED
swiftapi/anthropic.py
CHANGED
|
@@ -1,222 +1,236 @@
|
|
|
1
|
-
"""
|
|
2
|
-
SwiftAPI Attested Anthropic -
|
|
3
|
-
|
|
4
|
-
# Before (vibe coding):
|
|
5
|
-
from anthropic import Anthropic
|
|
6
|
-
client = Anthropic(api_key="sk-ant-...")
|
|
7
|
-
|
|
8
|
-
# After (attested):
|
|
9
|
-
from swiftapi import Anthropic
|
|
10
|
-
client = Anthropic(swiftapi_key="swiftapi_live_...", anthropic_key="sk-ant-...")
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
from . import
|
|
21
|
-
from .
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def
|
|
65
|
-
|
|
66
|
-
self.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
"
|
|
141
|
-
|
|
142
|
-
if
|
|
143
|
-
|
|
144
|
-
if
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
1
|
+
"""
|
|
2
|
+
SwiftAPI Attested Anthropic - Compatibility wrapper.
|
|
3
|
+
|
|
4
|
+
# Before (vibe coding):
|
|
5
|
+
from anthropic import Anthropic
|
|
6
|
+
client = Anthropic(api_key="sk-ant-...")
|
|
7
|
+
|
|
8
|
+
# After (attested):
|
|
9
|
+
from swiftapi import Anthropic
|
|
10
|
+
client = Anthropic(swiftapi_key="swiftapi_live_...", anthropic_key="sk-ant-...")
|
|
11
|
+
|
|
12
|
+
Compatibility wrapper for the documented nonstreaming text subset. Authorization is attested.
|
|
13
|
+
Provider keys are sent only to the provider. This wrapper does not establish native EOS.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import requests
|
|
17
|
+
from typing import List, Dict, Optional, Any
|
|
18
|
+
|
|
19
|
+
from . import __version__
|
|
20
|
+
from .exceptions import SwiftAPIError, AuthenticationError, NetworkError
|
|
21
|
+
from .verifier import (
|
|
22
|
+
verify_signature,
|
|
23
|
+
claim_attestation,
|
|
24
|
+
_action_fingerprint,
|
|
25
|
+
_snapshot_action_data,
|
|
26
|
+
_validate_action_binding,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ContentBlock:
|
|
31
|
+
"""Anthropic-compatible content block."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, text: str, type: str = "text"):
|
|
34
|
+
self.text = text
|
|
35
|
+
self.type = type
|
|
36
|
+
|
|
37
|
+
def __repr__(self):
|
|
38
|
+
truncated = self.text[:80] if self.text else ""
|
|
39
|
+
return f"ContentBlock(type='{self.type}', text='{truncated}')"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class MessageResponse:
|
|
43
|
+
"""Anthropic-compatible message response."""
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
content: str,
|
|
48
|
+
model: str,
|
|
49
|
+
role: str = "assistant",
|
|
50
|
+
stop_reason: str = None,
|
|
51
|
+
attested: bool = False,
|
|
52
|
+
jti: str = None,
|
|
53
|
+
):
|
|
54
|
+
self.content = [ContentBlock(text=content)] if content else [ContentBlock(text="")]
|
|
55
|
+
self.model = model
|
|
56
|
+
self.role = role
|
|
57
|
+
self.stop_reason = stop_reason
|
|
58
|
+
self.classification = "GATE_BLOCK" if not attested else "UNCLASSIFIED_PROVIDER_OUTPUT"
|
|
59
|
+
self.native_eos_verified = False
|
|
60
|
+
self.attested = attested
|
|
61
|
+
self.jti = jti
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def text(self):
|
|
65
|
+
"""Convenience: response.text instead of response.content[0].text"""
|
|
66
|
+
return self.content[0].text if self.content else ""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class Messages:
|
|
70
|
+
"""Handles messages.create() calls."""
|
|
71
|
+
|
|
72
|
+
def __init__(self, swiftapi_key: str, anthropic_key: str, authority_url: str, timeout: int):
|
|
73
|
+
self._swiftapi_key = swiftapi_key
|
|
74
|
+
self._anthropic_key = anthropic_key
|
|
75
|
+
from urllib.parse import urlparse
|
|
76
|
+
parsed = urlparse(authority_url)
|
|
77
|
+
if parsed.scheme != "https" or not parsed.hostname or parsed.username or parsed.password or parsed.query or parsed.fragment:
|
|
78
|
+
raise SwiftAPIError("Authority URL must use HTTPS without embedded credentials")
|
|
79
|
+
self._authority_url = authority_url.rstrip("/")
|
|
80
|
+
self._timeout = timeout
|
|
81
|
+
self._session = requests.Session()
|
|
82
|
+
|
|
83
|
+
def create(
|
|
84
|
+
self,
|
|
85
|
+
model: str,
|
|
86
|
+
messages: List[Dict[str, Any]],
|
|
87
|
+
max_tokens: int = 1024,
|
|
88
|
+
temperature: Optional[float] = None,
|
|
89
|
+
system: Optional[str] = None,
|
|
90
|
+
**kwargs,
|
|
91
|
+
) -> MessageResponse:
|
|
92
|
+
"""
|
|
93
|
+
Create an attested message completion.
|
|
94
|
+
|
|
95
|
+
Same interface as anthropic.messages.create().
|
|
96
|
+
Step 1: Attest with SwiftAPI (governance).
|
|
97
|
+
Step 2: Call Anthropic directly with your key (execution).
|
|
98
|
+
|
|
99
|
+
If governance denies, content is empty and classification is GATE_BLOCK.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
model: Model name (e.g. "claude-sonnet-4-5-20250929")
|
|
103
|
+
messages: List of message dicts with role and content
|
|
104
|
+
max_tokens: Max tokens in response
|
|
105
|
+
temperature: Sampling temperature
|
|
106
|
+
system: System prompt
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
MessageResponse with .content[0].text
|
|
110
|
+
"""
|
|
111
|
+
payload = {
|
|
112
|
+
"model": model,
|
|
113
|
+
"messages": messages,
|
|
114
|
+
"max_tokens": max_tokens,
|
|
115
|
+
}
|
|
116
|
+
if temperature is not None:
|
|
117
|
+
payload["temperature"] = temperature
|
|
118
|
+
if system is not None:
|
|
119
|
+
payload["system"] = system
|
|
120
|
+
if kwargs.get("stream"):
|
|
121
|
+
raise SwiftAPIError("Streaming is unsupported by this compatibility wrapper")
|
|
122
|
+
payload.update(kwargs)
|
|
123
|
+
payload = _snapshot_action_data(payload)
|
|
124
|
+
expected_fingerprint = _action_fingerprint(payload, truncated=True)
|
|
125
|
+
|
|
126
|
+
# Step 1: Attest with SwiftAPI — governance check
|
|
127
|
+
try:
|
|
128
|
+
attest_resp = self._session.post(
|
|
129
|
+
f"{self._authority_url}/attest",
|
|
130
|
+
json={"action_type": "chat_completion", "action_data": payload},
|
|
131
|
+
headers={
|
|
132
|
+
"X-SwiftAPI-Authority": self._swiftapi_key,
|
|
133
|
+
"Content-Type": "application/json",
|
|
134
|
+
"User-Agent": f"swiftapi-python/{__version__}",
|
|
135
|
+
},
|
|
136
|
+
timeout=self._timeout,
|
|
137
|
+
allow_redirects=False,
|
|
138
|
+
)
|
|
139
|
+
except requests.exceptions.RequestException:
|
|
140
|
+
raise NetworkError("Attestation request failed") from None
|
|
141
|
+
|
|
142
|
+
if attest_resp.status_code == 403:
|
|
143
|
+
raise AuthenticationError("Invalid SwiftAPI authority key")
|
|
144
|
+
if attest_resp.status_code == 429:
|
|
145
|
+
raise SwiftAPIError("Rate limit exceeded")
|
|
146
|
+
if attest_resp.status_code != 200:
|
|
147
|
+
raise SwiftAPIError("Attestation failed")
|
|
148
|
+
|
|
149
|
+
attestation = attest_resp.json()
|
|
150
|
+
verify_signature(attestation)
|
|
151
|
+
if not _validate_action_binding(attestation, "chat_completion", expected_fingerprint):
|
|
152
|
+
return MessageResponse(content="", model=model)
|
|
153
|
+
|
|
154
|
+
claim_attestation(attestation)
|
|
155
|
+
|
|
156
|
+
# Step 2: Call Anthropic directly — user's own key, user's own credits
|
|
157
|
+
anthropic_payload = payload
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
anthropic_resp = self._session.post(
|
|
161
|
+
"https://api.anthropic.com/v1/messages",
|
|
162
|
+
json=anthropic_payload,
|
|
163
|
+
headers={
|
|
164
|
+
"x-api-key": self._anthropic_key,
|
|
165
|
+
"anthropic-version": "2023-06-01",
|
|
166
|
+
"Content-Type": "application/json",
|
|
167
|
+
"User-Agent": f"swiftapi-python/{__version__}",
|
|
168
|
+
},
|
|
169
|
+
timeout=self._timeout,
|
|
170
|
+
allow_redirects=False,
|
|
171
|
+
)
|
|
172
|
+
except requests.exceptions.RequestException:
|
|
173
|
+
raise NetworkError("Anthropic request failed") from None
|
|
174
|
+
|
|
175
|
+
if anthropic_resp.status_code == 401:
|
|
176
|
+
raise AuthenticationError("Invalid Anthropic API key")
|
|
177
|
+
if anthropic_resp.status_code != 200:
|
|
178
|
+
raise SwiftAPIError("Anthropic error")
|
|
179
|
+
|
|
180
|
+
data = anthropic_resp.json()
|
|
181
|
+
content_blocks = data.get("content", [])
|
|
182
|
+
text = content_blocks[0].get("text", "") if content_blocks else ""
|
|
183
|
+
|
|
184
|
+
result = MessageResponse(
|
|
185
|
+
content=text,
|
|
186
|
+
model=model,
|
|
187
|
+
role=data.get("role", "assistant"),
|
|
188
|
+
stop_reason=data.get("stop_reason"),
|
|
189
|
+
attested=True,
|
|
190
|
+
jti=attestation.get("jti"),
|
|
191
|
+
)
|
|
192
|
+
result.raw_response = data
|
|
193
|
+
return result
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class Anthropic:
|
|
197
|
+
"""
|
|
198
|
+
Compatibility wrapper for anthropic.Anthropic.
|
|
199
|
+
|
|
200
|
+
Every completion is cryptographically attested via SwiftAPI.
|
|
201
|
+
SwiftAPI governs. Anthropic executes. You bring both keys.
|
|
202
|
+
|
|
203
|
+
Usage:
|
|
204
|
+
from swiftapi import Anthropic
|
|
205
|
+
|
|
206
|
+
client = Anthropic(
|
|
207
|
+
swiftapi_key="swiftapi_live_...",
|
|
208
|
+
anthropic_key="sk-ant-..."
|
|
209
|
+
)
|
|
210
|
+
r = client.messages.create(
|
|
211
|
+
model="claude-sonnet-4-5-20250929",
|
|
212
|
+
max_tokens=1024,
|
|
213
|
+
messages=[{"role": "user", "content": "Hello"}]
|
|
214
|
+
)
|
|
215
|
+
print(r.content[0].text)
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
def __init__(
|
|
219
|
+
self,
|
|
220
|
+
swiftapi_key: str = None,
|
|
221
|
+
anthropic_key: str = None,
|
|
222
|
+
api_key: str = None,
|
|
223
|
+
authority_url: str = "https://swiftapi.ai",
|
|
224
|
+
timeout: int = 60,
|
|
225
|
+
**kwargs,
|
|
226
|
+
):
|
|
227
|
+
if not swiftapi_key:
|
|
228
|
+
raise AuthenticationError("swiftapi_key is required")
|
|
229
|
+
if not swiftapi_key.startswith("swiftapi_live_"):
|
|
230
|
+
raise AuthenticationError("Invalid key format: must start with 'swiftapi_live_'")
|
|
231
|
+
|
|
232
|
+
ant_key = anthropic_key or api_key
|
|
233
|
+
if not ant_key:
|
|
234
|
+
raise AuthenticationError("anthropic_key is required (your Anthropic API key)")
|
|
235
|
+
|
|
236
|
+
self.messages = Messages(swiftapi_key, ant_key, authority_url, timeout)
|