astra-sdk 1.0.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.
- astra_sdk-1.0.0/MANIFEST.in +3 -0
- astra_sdk-1.0.0/PKG-INFO +32 -0
- astra_sdk-1.0.0/astra_sdk.egg-info/PKG-INFO +32 -0
- astra_sdk-1.0.0/astra_sdk.egg-info/SOURCES.txt +21 -0
- astra_sdk-1.0.0/astra_sdk.egg-info/dependency_links.txt +1 -0
- astra_sdk-1.0.0/astra_sdk.egg-info/entry_points.txt +2 -0
- astra_sdk-1.0.0/astra_sdk.egg-info/requires.txt +11 -0
- astra_sdk-1.0.0/astra_sdk.egg-info/top_level.txt +1 -0
- astra_sdk-1.0.0/codeastra/__init__.py +434 -0
- astra_sdk-1.0.0/codeastra/async_client.py +192 -0
- astra_sdk-1.0.0/codeastra/cli.py +253 -0
- astra_sdk-1.0.0/codeastra/client.py +158 -0
- astra_sdk-1.0.0/codeastra/config.py +73 -0
- astra_sdk-1.0.0/codeastra/decorators.py +83 -0
- astra_sdk-1.0.0/codeastra/detector.py +157 -0
- astra_sdk-1.0.0/codeastra/exceptions.py +25 -0
- astra_sdk-1.0.0/codeastra/patch.py +429 -0
- astra_sdk-1.0.0/codeastra/progress.py +412 -0
- astra_sdk-1.0.0/codeastra/py.typed +1 -0
- astra_sdk-1.0.0/codeastra/session.py +170 -0
- astra_sdk-1.0.0/codeastra/types.py +104 -0
- astra_sdk-1.0.0/pyproject.toml +52 -0
- astra_sdk-1.0.0/setup.cfg +4 -0
astra_sdk-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: astra-sdk
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: AI agent safety infrastructure. One line to connect.
|
|
5
|
+
Author-email: Codeastra <hello@codeastra.dev>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://codeastra.dev
|
|
8
|
+
Project-URL: Documentation, https://docs.codeastra.dev
|
|
9
|
+
Project-URL: Repository, https://github.com/codeastra/codeastra-python
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/codeastra/codeastra-python/issues
|
|
11
|
+
Keywords: ai,agents,safety,llm,openai,anthropic
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: requests>=2.28.0
|
|
24
|
+
Requires-Dist: httpx>=0.25.0
|
|
25
|
+
Provides-Extra: all
|
|
26
|
+
Requires-Dist: openai>=1.0.0; extra == "all"
|
|
27
|
+
Requires-Dist: anthropic>=0.20.0; extra == "all"
|
|
28
|
+
Requires-Dist: boto3>=1.26.0; extra == "all"
|
|
29
|
+
Requires-Dist: google-generativeai>=0.4.0; extra == "all"
|
|
30
|
+
Requires-Dist: langchain>=0.1.0; extra == "all"
|
|
31
|
+
Requires-Dist: langchain-openai>=0.1.0; extra == "all"
|
|
32
|
+
Requires-Dist: langchain-anthropic>=0.1.0; extra == "all"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: astra-sdk
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: AI agent safety infrastructure. One line to connect.
|
|
5
|
+
Author-email: Codeastra <hello@codeastra.dev>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://codeastra.dev
|
|
8
|
+
Project-URL: Documentation, https://docs.codeastra.dev
|
|
9
|
+
Project-URL: Repository, https://github.com/codeastra/codeastra-python
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/codeastra/codeastra-python/issues
|
|
11
|
+
Keywords: ai,agents,safety,llm,openai,anthropic
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: requests>=2.28.0
|
|
24
|
+
Requires-Dist: httpx>=0.25.0
|
|
25
|
+
Provides-Extra: all
|
|
26
|
+
Requires-Dist: openai>=1.0.0; extra == "all"
|
|
27
|
+
Requires-Dist: anthropic>=0.20.0; extra == "all"
|
|
28
|
+
Requires-Dist: boto3>=1.26.0; extra == "all"
|
|
29
|
+
Requires-Dist: google-generativeai>=0.4.0; extra == "all"
|
|
30
|
+
Requires-Dist: langchain>=0.1.0; extra == "all"
|
|
31
|
+
Requires-Dist: langchain-openai>=0.1.0; extra == "all"
|
|
32
|
+
Requires-Dist: langchain-anthropic>=0.1.0; extra == "all"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MANIFEST.in
|
|
2
|
+
pyproject.toml
|
|
3
|
+
astra_sdk.egg-info/PKG-INFO
|
|
4
|
+
astra_sdk.egg-info/SOURCES.txt
|
|
5
|
+
astra_sdk.egg-info/dependency_links.txt
|
|
6
|
+
astra_sdk.egg-info/entry_points.txt
|
|
7
|
+
astra_sdk.egg-info/requires.txt
|
|
8
|
+
astra_sdk.egg-info/top_level.txt
|
|
9
|
+
codeastra/__init__.py
|
|
10
|
+
codeastra/async_client.py
|
|
11
|
+
codeastra/cli.py
|
|
12
|
+
codeastra/client.py
|
|
13
|
+
codeastra/config.py
|
|
14
|
+
codeastra/decorators.py
|
|
15
|
+
codeastra/detector.py
|
|
16
|
+
codeastra/exceptions.py
|
|
17
|
+
codeastra/patch.py
|
|
18
|
+
codeastra/progress.py
|
|
19
|
+
codeastra/py.typed
|
|
20
|
+
codeastra/session.py
|
|
21
|
+
codeastra/types.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
codeastra
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Codeastra SDK
|
|
3
|
+
═════════════
|
|
4
|
+
AI agent safety infrastructure. One line to connect.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
import codeastra
|
|
8
|
+
|
|
9
|
+
codeastra.init(
|
|
10
|
+
api_key = "sk-guard-...",
|
|
11
|
+
goal = codeastra.Goal.approvals(100),
|
|
12
|
+
auto_deploy = True,
|
|
13
|
+
real_connections = codeastra.RealConnections(
|
|
14
|
+
stripe_secret_key = "sk_live_...",
|
|
15
|
+
sendgrid_api_key = "SG.xxx",
|
|
16
|
+
)
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# Your existing agent code — ZERO changes
|
|
20
|
+
from openai import OpenAI
|
|
21
|
+
client = OpenAI(api_key="sk-...")
|
|
22
|
+
# Every tool call now runs through Twin World automatically
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import logging
|
|
28
|
+
import uuid
|
|
29
|
+
from typing import Callable, Optional, Union
|
|
30
|
+
|
|
31
|
+
from .exceptions import (
|
|
32
|
+
AstraError, AstraAuthError, AstraConnectionError,
|
|
33
|
+
AstraSessionError, AstraGoalError, AstraWorldError,
|
|
34
|
+
AstraDeployError, AstraRateLimitError,
|
|
35
|
+
)
|
|
36
|
+
from .types import Goal, RealConnections, SessionConfig, SessionResult
|
|
37
|
+
from .session import Session
|
|
38
|
+
from .decorators import agent
|
|
39
|
+
from .client import AstraClient
|
|
40
|
+
from .async_client import AsyncAstraClient
|
|
41
|
+
from .progress import (
|
|
42
|
+
WorldWatcher, AsyncWorldWatcher,
|
|
43
|
+
TickEvent, GoalReachedEvent, ViolationEvent,
|
|
44
|
+
watch, awatch,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
__version__ = "1.0.0"
|
|
48
|
+
__all__ = [
|
|
49
|
+
"init", "connect", "session", "create_world", "worlds",
|
|
50
|
+
"report", "deploy", "fork", "compare", "status",
|
|
51
|
+
"Session", "Goal", "RealConnections", "agent",
|
|
52
|
+
"AstraError", "AstraAuthError", "AstraConnectionError",
|
|
53
|
+
"AsyncAstraClient",
|
|
54
|
+
"WorldWatcher", "AsyncWorldWatcher",
|
|
55
|
+
"TickEvent", "GoalReachedEvent", "ViolationEvent",
|
|
56
|
+
"watch", "awatch",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
log = logging.getLogger("codeastra")
|
|
60
|
+
|
|
61
|
+
# ── Global state ──────────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
_state: dict = {
|
|
64
|
+
"api_key": "",
|
|
65
|
+
"proxy_url": "https://proxy.codeastra.dev",
|
|
66
|
+
"dev_session": "",
|
|
67
|
+
"patched_providers":[],
|
|
68
|
+
"active_session": None,
|
|
69
|
+
"initialized": False,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _s(key: str, default=None):
|
|
74
|
+
return _state.get(key, default)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# ── Main API ──────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
def init(
|
|
80
|
+
api_key: str = "",
|
|
81
|
+
proxy_url: str = "",
|
|
82
|
+
goal: Optional[Union[Goal, dict]] = None,
|
|
83
|
+
auto_deploy: bool = False,
|
|
84
|
+
real_connections: Optional[Union[RealConnections, dict]] = None,
|
|
85
|
+
world_id: str = "",
|
|
86
|
+
domain: str = "general",
|
|
87
|
+
reserves: float = 5_000_000.0,
|
|
88
|
+
dev_session: str = "",
|
|
89
|
+
name: str = "sdk-session",
|
|
90
|
+
silent: bool = False,
|
|
91
|
+
patch: bool = True,
|
|
92
|
+
) -> SessionResult:
|
|
93
|
+
"""
|
|
94
|
+
Initialize Codeastra. Patches all detected AI providers and creates
|
|
95
|
+
a Twin World session. Call this once at the start of your agent script.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
api_key: Your Codeastra API key (or set ASTRA_API_KEY env var)
|
|
99
|
+
proxy_url: Proxy URL (default: https://proxy.codeastra.dev)
|
|
100
|
+
goal: What success looks like — Goal.approvals(100), etc.
|
|
101
|
+
auto_deploy: Auto-deploy to reality when goal is met
|
|
102
|
+
real_connections: Stripe, SendGrid, DB credentials for auto-deploy
|
|
103
|
+
world_id: Custom AI-generated world ID from create_world()
|
|
104
|
+
domain: Built-in physics domain (insurance, general, etc.)
|
|
105
|
+
reserves: Initial capital for physics simulation
|
|
106
|
+
dev_session: Optional session identifier (auto-generated if not set)
|
|
107
|
+
name: Session name for display
|
|
108
|
+
silent: Suppress console output
|
|
109
|
+
patch: Auto-patch all detected AI providers (default: True)
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
SessionResult with session info, detected providers, and proxy URL
|
|
113
|
+
"""
|
|
114
|
+
from . import config as _cfg, patch as _patch, detector as _det
|
|
115
|
+
|
|
116
|
+
# Load API key from env/config if not provided
|
|
117
|
+
if not api_key:
|
|
118
|
+
api_key = _cfg.get_api_key()
|
|
119
|
+
if not api_key:
|
|
120
|
+
raise AstraAuthError(
|
|
121
|
+
"API key required. Pass api_key= or set ASTRA_API_KEY env var.\n"
|
|
122
|
+
"Get your key at app.codeastra.dev"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Load proxy URL
|
|
126
|
+
if not proxy_url:
|
|
127
|
+
proxy_url = _cfg.get_proxy_url()
|
|
128
|
+
|
|
129
|
+
# Normalize goal
|
|
130
|
+
if isinstance(goal, dict):
|
|
131
|
+
goal = _dict_to_goal(goal)
|
|
132
|
+
|
|
133
|
+
# Normalize real_connections
|
|
134
|
+
if isinstance(real_connections, dict):
|
|
135
|
+
real_connections = _dict_to_connections(real_connections)
|
|
136
|
+
|
|
137
|
+
# Update global state
|
|
138
|
+
session_key = dev_session or str(uuid.uuid4())
|
|
139
|
+
_state.update({
|
|
140
|
+
"api_key": api_key,
|
|
141
|
+
"proxy_url": proxy_url,
|
|
142
|
+
"dev_session": session_key,
|
|
143
|
+
"initialized": True,
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
# Auto-detect and patch providers
|
|
147
|
+
patched = []
|
|
148
|
+
if patch:
|
|
149
|
+
patched = _patch.apply_all(api_key, proxy_url, session_key)
|
|
150
|
+
_state["patched_providers"] = patched
|
|
151
|
+
if not silent and patched:
|
|
152
|
+
print(f"[Astra] Connected providers: {', '.join(patched)}")
|
|
153
|
+
|
|
154
|
+
# Create session on proxy
|
|
155
|
+
config = SessionConfig(
|
|
156
|
+
api_key = api_key,
|
|
157
|
+
proxy_url = proxy_url,
|
|
158
|
+
dev_session = session_key,
|
|
159
|
+
goal = goal,
|
|
160
|
+
auto_deploy = auto_deploy,
|
|
161
|
+
real_connections = real_connections,
|
|
162
|
+
world_id = world_id,
|
|
163
|
+
domain = domain,
|
|
164
|
+
reserves = reserves,
|
|
165
|
+
name = name,
|
|
166
|
+
silent = silent,
|
|
167
|
+
)
|
|
168
|
+
sess = Session(config)
|
|
169
|
+
result = sess.start()
|
|
170
|
+
_state["active_session"] = sess
|
|
171
|
+
|
|
172
|
+
# Start real-time watcher if requested or callbacks provided
|
|
173
|
+
if watch_progress or on_tick or on_goal or on_violation:
|
|
174
|
+
from .progress import WorldWatcher as _WW
|
|
175
|
+
watcher = _WW(
|
|
176
|
+
api_key = api_key,
|
|
177
|
+
dev_session = session_key,
|
|
178
|
+
proxy_url = proxy_url,
|
|
179
|
+
on_tick = on_tick,
|
|
180
|
+
on_goal = on_goal,
|
|
181
|
+
on_violation = on_violation,
|
|
182
|
+
silent = silent,
|
|
183
|
+
)
|
|
184
|
+
watcher.start()
|
|
185
|
+
_state["watcher"] = watcher
|
|
186
|
+
|
|
187
|
+
return result
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def connect(
|
|
191
|
+
api_key: str = "",
|
|
192
|
+
**kwargs,
|
|
193
|
+
) -> SessionResult:
|
|
194
|
+
"""
|
|
195
|
+
Alias for init(). Auto-detects everything.
|
|
196
|
+
|
|
197
|
+
connect() without arguments reads from env vars / ~/.codeastra/config.json
|
|
198
|
+
"""
|
|
199
|
+
return init(api_key=api_key, **kwargs)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# ── Session shortcuts ─────────────────────────────────────────────────────────
|
|
203
|
+
|
|
204
|
+
def session(
|
|
205
|
+
goal: Optional[Union[Goal, dict]] = None,
|
|
206
|
+
auto_deploy: bool = False,
|
|
207
|
+
real_connections: Optional[Union[RealConnections, dict]] = None,
|
|
208
|
+
**kwargs,
|
|
209
|
+
) -> Session:
|
|
210
|
+
"""
|
|
211
|
+
Create a new session context manager.
|
|
212
|
+
|
|
213
|
+
Usage:
|
|
214
|
+
with codeastra.session(goal=Goal.approvals(100)) as s:
|
|
215
|
+
agent.run()
|
|
216
|
+
print(s.report())
|
|
217
|
+
"""
|
|
218
|
+
api_key = _s("api_key") or kwargs.pop("api_key", "")
|
|
219
|
+
proxy_url = _s("proxy_url", "https://proxy.codeastra.dev")
|
|
220
|
+
|
|
221
|
+
if isinstance(goal, dict):
|
|
222
|
+
goal = _dict_to_goal(goal)
|
|
223
|
+
if isinstance(real_connections, dict):
|
|
224
|
+
real_connections = _dict_to_connections(real_connections)
|
|
225
|
+
|
|
226
|
+
config = SessionConfig(
|
|
227
|
+
api_key = api_key,
|
|
228
|
+
proxy_url = proxy_url,
|
|
229
|
+
goal = goal,
|
|
230
|
+
auto_deploy = auto_deploy,
|
|
231
|
+
real_connections = real_connections,
|
|
232
|
+
**kwargs,
|
|
233
|
+
)
|
|
234
|
+
return Session(config)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
# ── World Builder ─────────────────────────────────────────────────────────────
|
|
238
|
+
|
|
239
|
+
def create_world(description: str) -> dict:
|
|
240
|
+
"""
|
|
241
|
+
Create a custom AI-generated Twin World from plain English.
|
|
242
|
+
|
|
243
|
+
Claude generates physics rules, hard limits, and autonomous actors
|
|
244
|
+
specific to your business domain.
|
|
245
|
+
|
|
246
|
+
Usage:
|
|
247
|
+
world = codeastra.create_world(
|
|
248
|
+
"I'm building a claims processing agent for an insurance company.
|
|
249
|
+
Approving claims depletes reserves. Fraudulent claims increase risk.
|
|
250
|
+
Regulator intervenes if risk exceeds 0.8."
|
|
251
|
+
)
|
|
252
|
+
codeastra.init(api_key="...", world_id=world["world_id"])
|
|
253
|
+
"""
|
|
254
|
+
api_key = _s("api_key")
|
|
255
|
+
proxy_url = _s("proxy_url", "https://proxy.codeastra.dev")
|
|
256
|
+
if not api_key:
|
|
257
|
+
raise AstraAuthError("Call codeastra.init() or pass api_key first")
|
|
258
|
+
|
|
259
|
+
client = AstraClient(api_key, proxy_url)
|
|
260
|
+
result = client.create_world(description)
|
|
261
|
+
|
|
262
|
+
if not _s("silent"):
|
|
263
|
+
world_id = result.get("world_id", "?")
|
|
264
|
+
domain = result.get("domain_name", "?")
|
|
265
|
+
print(f"[Astra] World created: {domain} (id: {world_id})")
|
|
266
|
+
|
|
267
|
+
return result
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def worlds() -> list:
|
|
271
|
+
"""List all available worlds for your API key."""
|
|
272
|
+
api_key = _s("api_key")
|
|
273
|
+
proxy_url = _s("proxy_url", "https://proxy.codeastra.dev")
|
|
274
|
+
if not api_key:
|
|
275
|
+
raise AstraAuthError("Call codeastra.init() first")
|
|
276
|
+
return AstraClient(api_key, proxy_url).list_worlds().get("worlds", [])
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
# ── Session operations ────────────────────────────────────────────────────────
|
|
280
|
+
|
|
281
|
+
def report(dev_session: str = "") -> dict:
|
|
282
|
+
"""Get Twin World outcome report for current or specified session."""
|
|
283
|
+
sess = dev_session or _s("dev_session")
|
|
284
|
+
if not sess:
|
|
285
|
+
raise AstraSessionError("No active session. Call codeastra.init() first.")
|
|
286
|
+
return _client().get_report(sess)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def status(dev_session: str = "") -> dict:
|
|
290
|
+
"""Get current world state for active session."""
|
|
291
|
+
sess = dev_session or _s("dev_session")
|
|
292
|
+
if not sess:
|
|
293
|
+
raise AstraSessionError("No active session.")
|
|
294
|
+
return _client().get_session(sess)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def deploy(
|
|
298
|
+
real_connections: Optional[Union[RealConnections, dict]] = None,
|
|
299
|
+
dev_session: str = "",
|
|
300
|
+
) -> dict:
|
|
301
|
+
"""Manually deploy winning session actions to production."""
|
|
302
|
+
sess = dev_session or _s("dev_session")
|
|
303
|
+
if not sess:
|
|
304
|
+
raise AstraSessionError("No active session.")
|
|
305
|
+
if isinstance(real_connections, RealConnections):
|
|
306
|
+
real_connections = real_connections.to_dict()
|
|
307
|
+
return _client().deploy(sess, real_connections or {})
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def history(dev_session: str = "") -> dict:
|
|
311
|
+
"""Full tick-by-tick world evolution."""
|
|
312
|
+
sess = dev_session or _s("dev_session")
|
|
313
|
+
return _client().get_history(sess)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def actor_events(dev_session: str = "") -> dict:
|
|
317
|
+
"""Full history of autonomous actor reactions."""
|
|
318
|
+
sess = dev_session or _s("dev_session")
|
|
319
|
+
return _client().get_actor_events(sess)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def fork(
|
|
323
|
+
branches: list[str],
|
|
324
|
+
dev_session: str = "",
|
|
325
|
+
world_id: str = "",
|
|
326
|
+
) -> dict:
|
|
327
|
+
"""
|
|
328
|
+
Fork current session into parallel branches for strategy comparison.
|
|
329
|
+
|
|
330
|
+
Usage:
|
|
331
|
+
fork_result = codeastra.fork(["aggressive", "conservative", "balanced"])
|
|
332
|
+
# Run agent in each branch using the returned dev_session_keys
|
|
333
|
+
winner = codeastra.compare()
|
|
334
|
+
"""
|
|
335
|
+
sess = dev_session or _s("dev_session")
|
|
336
|
+
wid = world_id or _s("world_id", "")
|
|
337
|
+
return _client().fork(sess, branches, wid)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def compare(dev_session: str = "") -> dict:
|
|
341
|
+
"""Compare forked branches. Returns ranked results and winner."""
|
|
342
|
+
sess = dev_session or _s("dev_session")
|
|
343
|
+
return _client().compare(sess)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def sync_csv(path: str, table: str = "data", dev_session: str = "") -> dict:
|
|
347
|
+
"""Load CSV data into active Twin World session."""
|
|
348
|
+
sess = dev_session or _s("dev_session")
|
|
349
|
+
with open(path, "rb") as f:
|
|
350
|
+
return _client().sync_csv(sess, f.read(), table)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def sync_db(
|
|
354
|
+
connection_url: str,
|
|
355
|
+
query: str = "",
|
|
356
|
+
dev_session: str = "",
|
|
357
|
+
) -> dict:
|
|
358
|
+
"""Sync data from real database into Twin World session."""
|
|
359
|
+
sess = dev_session or _s("dev_session")
|
|
360
|
+
return _client().sync_db(sess, connection_url, query)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
async def async_init(
|
|
364
|
+
api_key: str = "",
|
|
365
|
+
**kwargs,
|
|
366
|
+
) -> SessionResult:
|
|
367
|
+
"""
|
|
368
|
+
Async version of init() for async agent frameworks.
|
|
369
|
+
Usage:
|
|
370
|
+
await codeastra.async_init(api_key="sk-guard-...")
|
|
371
|
+
"""
|
|
372
|
+
from .async_client import AsyncAstraClient as _ACC
|
|
373
|
+
from . import config as _cfg
|
|
374
|
+
if not api_key:
|
|
375
|
+
api_key = _cfg.get_api_key()
|
|
376
|
+
if not api_key:
|
|
377
|
+
raise AstraAuthError("api_key required")
|
|
378
|
+
# init() is sync but session creation can be done async separately
|
|
379
|
+
# For now delegate to sync init — patches happen synchronously
|
|
380
|
+
return init(api_key=api_key, **kwargs)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def disconnect() -> None:
|
|
384
|
+
"""Remove all provider patches and reset state."""
|
|
385
|
+
from . import patch as _patch
|
|
386
|
+
_patch.remove_all()
|
|
387
|
+
_state.update({
|
|
388
|
+
"initialized": False,
|
|
389
|
+
"patched_providers": [],
|
|
390
|
+
"active_session": None,
|
|
391
|
+
})
|
|
392
|
+
print("[Astra] Disconnected. All patches removed.")
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def health() -> dict:
|
|
396
|
+
"""Check proxy and engine health."""
|
|
397
|
+
proxy_url = _s("proxy_url", "https://proxy.codeastra.dev")
|
|
398
|
+
api_key = _s("api_key", "")
|
|
399
|
+
try:
|
|
400
|
+
import requests
|
|
401
|
+
r = requests.get(f"{proxy_url}/health", timeout=5)
|
|
402
|
+
return r.json() if r.ok else {"status": "unreachable"}
|
|
403
|
+
except Exception as e:
|
|
404
|
+
return {"status": "unreachable", "error": str(e)}
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
408
|
+
|
|
409
|
+
def _client() -> AstraClient:
|
|
410
|
+
api_key = _s("api_key")
|
|
411
|
+
proxy_url = _s("proxy_url", "https://proxy.codeastra.dev")
|
|
412
|
+
if not api_key:
|
|
413
|
+
raise AstraAuthError("Call codeastra.init() first")
|
|
414
|
+
return AstraClient(api_key, proxy_url)
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def _dict_to_goal(d: dict) -> Goal:
|
|
418
|
+
gtype = d.get("type", "")
|
|
419
|
+
if gtype == "world_state":
|
|
420
|
+
return Goal("world_state", d.get("value", 0),
|
|
421
|
+
d.get("field", ""), d.get("op", "gte"))
|
|
422
|
+
return Goal(gtype, d.get("value", 0))
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def _dict_to_connections(d: dict) -> RealConnections:
|
|
426
|
+
return RealConnections(
|
|
427
|
+
stripe_secret_key = d.get("stripe_secret_key", ""),
|
|
428
|
+
sendgrid_api_key = d.get("sendgrid_api_key", ""),
|
|
429
|
+
resend_api_key = d.get("resend_api_key", ""),
|
|
430
|
+
slack_bot_token = d.get("slack_bot_token", ""),
|
|
431
|
+
database_url = d.get("database_url", ""),
|
|
432
|
+
from_email = d.get("from_email", ""),
|
|
433
|
+
webhook_url = d.get("webhook_url", ""),
|
|
434
|
+
)
|