evolvingmachines-evolve 0.0.55.dev1355__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.
- bridge/__init__.py +5 -0
- bridge/dist/bridge.bundle.cjs +1275 -0
- evolve/__init__.py +819 -0
- evolve/_http.py +71 -0
- evolve/agent.py +896 -0
- evolve/bridge.py +509 -0
- evolve/browser_credentials.py +265 -0
- evolve/browser_profiles.py +95 -0
- evolve/config.py +600 -0
- evolve/hosted.py +8958 -0
- evolve/integrations.py +173 -0
- evolve/managed_secrets.py +175 -0
- evolve/pipeline/__init__.py +59 -0
- evolve/pipeline/pipeline.py +512 -0
- evolve/pipeline/types.py +286 -0
- evolve/prompts/__init__.py +132 -0
- evolve/prompts/agent_md/judge.md +30 -0
- evolve/prompts/agent_md/reduce.md +7 -0
- evolve/prompts/agent_md/verify.md +33 -0
- evolve/prompts/user/judge.md +1 -0
- evolve/prompts/user/retry_feedback.md +9 -0
- evolve/prompts/user/verify.md +1 -0
- evolve/py.typed +0 -0
- evolve/results.py +315 -0
- evolve/retry.py +133 -0
- evolve/schema.py +107 -0
- evolve/sessions_client.py +167 -0
- evolve/storage_client.py +178 -0
- evolve/swarm/__init__.py +75 -0
- evolve/swarm/results.py +140 -0
- evolve/swarm/swarm.py +2116 -0
- evolve/swarm/types.py +241 -0
- evolve/utils.py +227 -0
- evolvingmachines_evolve-0.0.55.dev1355.dist-info/METADATA +52 -0
- evolvingmachines_evolve-0.0.55.dev1355.dist-info/RECORD +38 -0
- evolvingmachines_evolve-0.0.55.dev1355.dist-info/WHEEL +5 -0
- evolvingmachines_evolve-0.0.55.dev1355.dist-info/licenses/LICENSE +201 -0
- evolvingmachines_evolve-0.0.55.dev1355.dist-info/top_level.txt +2 -0
evolve/integrations.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Managed integrations async helpers."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
from .bridge import BridgeManager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class IntegrationAuthResult:
|
|
11
|
+
"""Result from an integration auth request."""
|
|
12
|
+
url: str
|
|
13
|
+
account_id: Optional[str] = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class IntegrationAccount:
|
|
18
|
+
"""Connected account for a managed app integration."""
|
|
19
|
+
user_id: str
|
|
20
|
+
app: str
|
|
21
|
+
status: str
|
|
22
|
+
app_name: Optional[str] = None
|
|
23
|
+
app_icon: Optional[str] = None
|
|
24
|
+
account_label: Optional[str] = None
|
|
25
|
+
account_id: Optional[str] = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class IntegrationAccountUpdateResult:
|
|
30
|
+
"""Result from updating a connected account."""
|
|
31
|
+
success: bool
|
|
32
|
+
account_id: str
|
|
33
|
+
account_label: Optional[str] = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class IntegrationAccountDeleteResult:
|
|
38
|
+
"""Result from deleting a connected account."""
|
|
39
|
+
success: bool
|
|
40
|
+
account_id: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def auth(
|
|
44
|
+
*,
|
|
45
|
+
user_id: str,
|
|
46
|
+
app: str,
|
|
47
|
+
account_label: Optional[str] = None,
|
|
48
|
+
api_key: Optional[str] = None,
|
|
49
|
+
dashboard_url: Optional[str] = None,
|
|
50
|
+
) -> IntegrationAuthResult:
|
|
51
|
+
"""Create an auth URL for one app account."""
|
|
52
|
+
bridge = BridgeManager()
|
|
53
|
+
try:
|
|
54
|
+
await bridge.start()
|
|
55
|
+
params: Dict[str, object] = {'user_id': user_id, 'app': app}
|
|
56
|
+
if account_label is not None:
|
|
57
|
+
params['account_label'] = account_label
|
|
58
|
+
if api_key:
|
|
59
|
+
params['api_key'] = api_key
|
|
60
|
+
if dashboard_url:
|
|
61
|
+
params['dashboard_url'] = dashboard_url
|
|
62
|
+
response = await bridge.call('integrations_auth', params)
|
|
63
|
+
return IntegrationAuthResult(
|
|
64
|
+
url=response['url'],
|
|
65
|
+
account_id=response.get('account_id'),
|
|
66
|
+
)
|
|
67
|
+
finally:
|
|
68
|
+
await bridge.stop()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class _Accounts:
|
|
72
|
+
async def list(
|
|
73
|
+
self,
|
|
74
|
+
*,
|
|
75
|
+
user_ids: List[str],
|
|
76
|
+
app: Optional[str] = None,
|
|
77
|
+
statuses: Optional[List[str]] = None,
|
|
78
|
+
api_key: Optional[str] = None,
|
|
79
|
+
dashboard_url: Optional[str] = None,
|
|
80
|
+
) -> List[IntegrationAccount]:
|
|
81
|
+
"""List connected accounts for one or more integration users."""
|
|
82
|
+
bridge = BridgeManager()
|
|
83
|
+
try:
|
|
84
|
+
await bridge.start()
|
|
85
|
+
params: Dict[str, object] = {'user_ids': user_ids}
|
|
86
|
+
if app:
|
|
87
|
+
params['app'] = app
|
|
88
|
+
if statuses:
|
|
89
|
+
params['statuses'] = statuses
|
|
90
|
+
if api_key:
|
|
91
|
+
params['api_key'] = api_key
|
|
92
|
+
if dashboard_url:
|
|
93
|
+
params['dashboard_url'] = dashboard_url
|
|
94
|
+
response = await bridge.call('integrations_accounts_list', params)
|
|
95
|
+
return [
|
|
96
|
+
IntegrationAccount(
|
|
97
|
+
user_id=account['user_id'],
|
|
98
|
+
app=account['app'],
|
|
99
|
+
status=account['status'],
|
|
100
|
+
app_name=account.get('app_name'),
|
|
101
|
+
app_icon=account.get('app_icon'),
|
|
102
|
+
account_label=account.get('account_label'),
|
|
103
|
+
account_id=account.get('account_id'),
|
|
104
|
+
)
|
|
105
|
+
for account in response['accounts']
|
|
106
|
+
]
|
|
107
|
+
finally:
|
|
108
|
+
await bridge.stop()
|
|
109
|
+
|
|
110
|
+
async def update(
|
|
111
|
+
self,
|
|
112
|
+
*,
|
|
113
|
+
account_id: str,
|
|
114
|
+
account_label: Optional[str] = None,
|
|
115
|
+
api_key: Optional[str] = None,
|
|
116
|
+
dashboard_url: Optional[str] = None,
|
|
117
|
+
) -> IntegrationAccountUpdateResult:
|
|
118
|
+
"""Set, update, or clear a connected account label."""
|
|
119
|
+
bridge = BridgeManager()
|
|
120
|
+
try:
|
|
121
|
+
await bridge.start()
|
|
122
|
+
params: Dict[str, object] = {'account_id': account_id}
|
|
123
|
+
if account_label is not None:
|
|
124
|
+
params['account_label'] = account_label
|
|
125
|
+
if api_key:
|
|
126
|
+
params['api_key'] = api_key
|
|
127
|
+
if dashboard_url:
|
|
128
|
+
params['dashboard_url'] = dashboard_url
|
|
129
|
+
response = await bridge.call('integrations_account_update', params)
|
|
130
|
+
return IntegrationAccountUpdateResult(
|
|
131
|
+
success=response['success'],
|
|
132
|
+
account_id=response['account_id'],
|
|
133
|
+
account_label=response.get('account_label'),
|
|
134
|
+
)
|
|
135
|
+
finally:
|
|
136
|
+
await bridge.stop()
|
|
137
|
+
|
|
138
|
+
async def delete(
|
|
139
|
+
self,
|
|
140
|
+
*,
|
|
141
|
+
account_id: str,
|
|
142
|
+
api_key: Optional[str] = None,
|
|
143
|
+
dashboard_url: Optional[str] = None,
|
|
144
|
+
) -> IntegrationAccountDeleteResult:
|
|
145
|
+
"""Delete a connected account."""
|
|
146
|
+
bridge = BridgeManager()
|
|
147
|
+
try:
|
|
148
|
+
await bridge.start()
|
|
149
|
+
params: Dict[str, object] = {'account_id': account_id}
|
|
150
|
+
if api_key:
|
|
151
|
+
params['api_key'] = api_key
|
|
152
|
+
if dashboard_url:
|
|
153
|
+
params['dashboard_url'] = dashboard_url
|
|
154
|
+
response = await bridge.call('integrations_account_delete', params)
|
|
155
|
+
return IntegrationAccountDeleteResult(
|
|
156
|
+
success=response['success'],
|
|
157
|
+
account_id=response['account_id'],
|
|
158
|
+
)
|
|
159
|
+
finally:
|
|
160
|
+
await bridge.stop()
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
accounts = _Accounts()
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
__all__ = [
|
|
167
|
+
'auth',
|
|
168
|
+
'accounts',
|
|
169
|
+
'IntegrationAuthResult',
|
|
170
|
+
'IntegrationAccount',
|
|
171
|
+
'IntegrationAccountUpdateResult',
|
|
172
|
+
'IntegrationAccountDeleteResult',
|
|
173
|
+
]
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""Standalone managed secrets client."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import os
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
from . import _http
|
|
9
|
+
from .config import ManagedSecretsClientConfig
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
DEFAULT_DASHBOARD_URL = 'https://dashboard.evolvingmachines.ai'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class ManagedSecretMetadata:
|
|
17
|
+
id: str
|
|
18
|
+
name: str
|
|
19
|
+
allowed_hosts: List[str]
|
|
20
|
+
allowed_path_prefixes: List[str]
|
|
21
|
+
allowed_methods: List[str]
|
|
22
|
+
created_at: str
|
|
23
|
+
updated_at: str
|
|
24
|
+
last_used_at: Optional[str]
|
|
25
|
+
#: The row's label — secrets are unique by (name, label), 'default' when
|
|
26
|
+
#: none was chosen at store time. None: servers older than the label
|
|
27
|
+
#: lane omit it.
|
|
28
|
+
label: Optional[str] = None
|
|
29
|
+
#: The row's delivery mode: 'brokered' (the value never enters any
|
|
30
|
+
#: sandbox — placeholder env + egress-proxy swap toward allowed_hosts)
|
|
31
|
+
#: or 'direct' (the raw value is placed in the sandbox environment —
|
|
32
|
+
#: URL-parameter keys, gRPC, websockets). None: servers older than the
|
|
33
|
+
#: delivery lane omit it and always broker.
|
|
34
|
+
delivery: Optional[str] = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _metadata_from_dict(data: Dict[str, Any]) -> ManagedSecretMetadata:
|
|
38
|
+
return ManagedSecretMetadata(
|
|
39
|
+
id=data['id'],
|
|
40
|
+
name=data['name'],
|
|
41
|
+
allowed_hosts=list(data.get('allowedHosts') or data.get('allowed_hosts') or []),
|
|
42
|
+
allowed_path_prefixes=list(data.get('allowedPathPrefixes') or data.get('allowed_path_prefixes') or []),
|
|
43
|
+
allowed_methods=list(data.get('allowedMethods') or data.get('allowed_methods') or []),
|
|
44
|
+
created_at=data.get('createdAt') or data.get('created_at') or '',
|
|
45
|
+
updated_at=data.get('updatedAt') or data.get('updated_at') or '',
|
|
46
|
+
last_used_at=data.get('lastUsedAt') or data.get('last_used_at'),
|
|
47
|
+
label=data.get('label'),
|
|
48
|
+
delivery=data.get('delivery'),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class ManagedSecretWriteResult:
|
|
54
|
+
#: 'created' for a fresh (name, label) identity; 'updated' when the
|
|
55
|
+
#: request restated the stored value byte-for-byte and the row converged
|
|
56
|
+
#: (the one path where delivery and scoping are editable). A DIFFERENT
|
|
57
|
+
#: value under an existing identity never overwrites — the server
|
|
58
|
+
#: refuses typed (HTTP 409, code ``secret_exists``).
|
|
59
|
+
status: str
|
|
60
|
+
secret: ManagedSecretMetadata
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class ManagedSecretDeleteResult:
|
|
65
|
+
ok: bool
|
|
66
|
+
name: str
|
|
67
|
+
#: The label of the row that was deleted (resolution may have defaulted it).
|
|
68
|
+
label: str
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ManagedSecretsClient:
|
|
72
|
+
"""Dashboard-stored managed secrets: list metadata, set and delete.
|
|
73
|
+
|
|
74
|
+
``set`` speaks the API-key write door (``POST /api/managed-secrets``):
|
|
75
|
+
the VALUE rides the HTTPS body and is sealed server-side with the vault
|
|
76
|
+
cipher — the same posture the inline job-secrets door on job create
|
|
77
|
+
established; no read ever returns it. LLM provider keys (BYOK) are not
|
|
78
|
+
writable here — they gate billing and stay behind the signed-in
|
|
79
|
+
dashboard session.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def __init__(self, config: Optional[ManagedSecretsClientConfig] = None):
|
|
83
|
+
self.config = config or ManagedSecretsClientConfig()
|
|
84
|
+
|
|
85
|
+
async def __aenter__(self):
|
|
86
|
+
return self
|
|
87
|
+
|
|
88
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
89
|
+
await self.close()
|
|
90
|
+
|
|
91
|
+
async def close(self):
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
async def list(self) -> List[ManagedSecretMetadata]:
|
|
95
|
+
result = await self._request_json('/api/managed-secrets')
|
|
96
|
+
return [_metadata_from_dict(item) for item in result.get('secrets', [])]
|
|
97
|
+
|
|
98
|
+
async def set(
|
|
99
|
+
self,
|
|
100
|
+
*,
|
|
101
|
+
name: str,
|
|
102
|
+
value: str,
|
|
103
|
+
delivery: str,
|
|
104
|
+
label: Optional[str] = None,
|
|
105
|
+
allowed_hosts: Optional[List[str]] = None,
|
|
106
|
+
allowed_path_prefixes: Optional[List[str]] = None,
|
|
107
|
+
allowed_methods: Optional[List[str]] = None,
|
|
108
|
+
) -> ManagedSecretWriteResult:
|
|
109
|
+
"""Create or update an env secret (identity = (name, label)).
|
|
110
|
+
|
|
111
|
+
``delivery`` is required: 'brokered' needs the allowed_hosts /
|
|
112
|
+
allowed_path_prefixes / allowed_methods scoping triple, 'direct'
|
|
113
|
+
refuses it. An existing identity converges only on a byte-equal
|
|
114
|
+
restatement of the stored value; a different value refuses typed —
|
|
115
|
+
rotate by delete + set, or use another label.
|
|
116
|
+
"""
|
|
117
|
+
body: Dict[str, Any] = {'name': name, 'value': value, 'delivery': delivery}
|
|
118
|
+
if label is not None:
|
|
119
|
+
body['label'] = label
|
|
120
|
+
if allowed_hosts is not None:
|
|
121
|
+
body['allowed_hosts'] = list(allowed_hosts)
|
|
122
|
+
if allowed_path_prefixes is not None:
|
|
123
|
+
body['allowed_path_prefixes'] = list(allowed_path_prefixes)
|
|
124
|
+
if allowed_methods is not None:
|
|
125
|
+
body['allowed_methods'] = list(allowed_methods)
|
|
126
|
+
result = await self._request_json('/api/managed-secrets', method='POST', body=body)
|
|
127
|
+
return ManagedSecretWriteResult(
|
|
128
|
+
status=str(result.get('status') or ''),
|
|
129
|
+
secret=_metadata_from_dict(result.get('secret') or {}),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
async def delete(
|
|
133
|
+
self, *, name: str, label: Optional[str] = None
|
|
134
|
+
) -> ManagedSecretDeleteResult:
|
|
135
|
+
"""Delete an env secret by (name, label).
|
|
136
|
+
|
|
137
|
+
An omitted label resolves by the shared law: the 'default' row, else
|
|
138
|
+
the single row, else a typed ambiguity refusal naming every label.
|
|
139
|
+
"""
|
|
140
|
+
body: Dict[str, Any] = {'name': name}
|
|
141
|
+
if label is not None:
|
|
142
|
+
body['label'] = label
|
|
143
|
+
result = await self._request_json('/api/managed-secrets', method='DELETE', body=body)
|
|
144
|
+
return ManagedSecretDeleteResult(
|
|
145
|
+
ok=bool(result.get('ok')),
|
|
146
|
+
name=str(result.get('name') or name),
|
|
147
|
+
label=str(result.get('label') or ''),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
async def _request_json(
|
|
151
|
+
self,
|
|
152
|
+
path: str,
|
|
153
|
+
*,
|
|
154
|
+
method: str = 'GET',
|
|
155
|
+
body: Optional[Dict[str, Any]] = None,
|
|
156
|
+
) -> Dict[str, Any]:
|
|
157
|
+
return await asyncio.to_thread(
|
|
158
|
+
_http.request_json,
|
|
159
|
+
f'{_dashboard_base_url(self.config)}{path}',
|
|
160
|
+
api_key=_resolve_api_key(self.config),
|
|
161
|
+
error_prefix='Managed secrets',
|
|
162
|
+
method=method,
|
|
163
|
+
body=body,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _dashboard_base_url(config: ManagedSecretsClientConfig) -> str:
|
|
168
|
+
return (config.dashboard_url or os.environ.get('EVOLVE_DASHBOARD_URL') or DEFAULT_DASHBOARD_URL).rstrip('/')
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _resolve_api_key(config: ManagedSecretsClientConfig) -> str:
|
|
172
|
+
api_key = config.api_key or os.environ.get('EVOLVE_API_KEY')
|
|
173
|
+
if not api_key:
|
|
174
|
+
raise ValueError('Managed secrets require EVOLVE_API_KEY or ManagedSecretsClientConfig(api_key=...)')
|
|
175
|
+
return api_key
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Pipeline - Fluent API for chaining Swarm operations."""
|
|
2
|
+
|
|
3
|
+
from .pipeline import Pipeline, TerminalPipeline
|
|
4
|
+
from .types import (
|
|
5
|
+
# Step configs
|
|
6
|
+
MapConfig,
|
|
7
|
+
FilterConfig,
|
|
8
|
+
ReduceConfig,
|
|
9
|
+
# Results
|
|
10
|
+
StepResult,
|
|
11
|
+
PipelineResult,
|
|
12
|
+
# Events
|
|
13
|
+
PipelineEvents,
|
|
14
|
+
PipelineEventMap,
|
|
15
|
+
StepEvent,
|
|
16
|
+
StepStartEvent,
|
|
17
|
+
StepCompleteEvent,
|
|
18
|
+
StepErrorEvent,
|
|
19
|
+
ItemRetryEvent,
|
|
20
|
+
WorkerCompleteEvent,
|
|
21
|
+
VerifierCompleteEvent,
|
|
22
|
+
CandidateCompleteEvent,
|
|
23
|
+
JudgeCompleteEvent,
|
|
24
|
+
# Types
|
|
25
|
+
EmitOption,
|
|
26
|
+
EventName,
|
|
27
|
+
Step,
|
|
28
|
+
StepType,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
# Main classes
|
|
33
|
+
'Pipeline',
|
|
34
|
+
'TerminalPipeline',
|
|
35
|
+
# Step configs
|
|
36
|
+
'MapConfig',
|
|
37
|
+
'FilterConfig',
|
|
38
|
+
'ReduceConfig',
|
|
39
|
+
# Results
|
|
40
|
+
'StepResult',
|
|
41
|
+
'PipelineResult',
|
|
42
|
+
# Events
|
|
43
|
+
'PipelineEvents',
|
|
44
|
+
'PipelineEventMap',
|
|
45
|
+
'StepEvent',
|
|
46
|
+
'StepStartEvent',
|
|
47
|
+
'StepCompleteEvent',
|
|
48
|
+
'StepErrorEvent',
|
|
49
|
+
'ItemRetryEvent',
|
|
50
|
+
'WorkerCompleteEvent',
|
|
51
|
+
'VerifierCompleteEvent',
|
|
52
|
+
'CandidateCompleteEvent',
|
|
53
|
+
'JudgeCompleteEvent',
|
|
54
|
+
# Types
|
|
55
|
+
'EmitOption',
|
|
56
|
+
'EventName',
|
|
57
|
+
'Step',
|
|
58
|
+
'StepType',
|
|
59
|
+
]
|