planar 0.10.0__py3-none-any.whl → 0.12.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.
- planar/app.py +26 -6
- planar/cli.py +26 -0
- planar/data/__init__.py +1 -0
- planar/data/config.py +12 -1
- planar/data/connection.py +89 -4
- planar/data/dataset.py +13 -7
- planar/data/utils.py +145 -25
- planar/db/alembic/env.py +68 -57
- planar/db/alembic.ini +1 -1
- planar/files/storage/config.py +7 -1
- planar/routers/dataset_router.py +5 -1
- planar/routers/info.py +79 -36
- planar/scaffold_templates/pyproject.toml.j2 +1 -1
- planar/testing/fixtures.py +7 -4
- planar/testing/planar_test_client.py +8 -0
- planar/version.py +27 -0
- planar-0.12.0.dist-info/METADATA +202 -0
- {planar-0.10.0.dist-info → planar-0.12.0.dist-info}/RECORD +20 -71
- planar/ai/test_agent_serialization.py +0 -229
- planar/ai/test_agent_tool_step_display.py +0 -78
- planar/data/test_dataset.py +0 -358
- planar/files/storage/test_azure_blob.py +0 -435
- planar/files/storage/test_local_directory.py +0 -162
- planar/files/storage/test_s3.py +0 -299
- planar/files/test_files.py +0 -282
- planar/human/test_human.py +0 -385
- planar/logging/test_formatter.py +0 -327
- planar/modeling/mixins/test_auditable.py +0 -97
- planar/modeling/mixins/test_timestamp.py +0 -134
- planar/modeling/mixins/test_uuid_primary_key.py +0 -52
- planar/routers/test_agents_router.py +0 -174
- planar/routers/test_dataset_router.py +0 -429
- planar/routers/test_files_router.py +0 -49
- planar/routers/test_object_config_router.py +0 -367
- planar/routers/test_routes_security.py +0 -168
- planar/routers/test_rule_router.py +0 -470
- planar/routers/test_workflow_router.py +0 -564
- planar/rules/test_data/account_dormancy_management.json +0 -223
- planar/rules/test_data/airline_loyalty_points_calculator.json +0 -262
- planar/rules/test_data/applicant_risk_assessment.json +0 -435
- planar/rules/test_data/booking_fraud_detection.json +0 -407
- planar/rules/test_data/cellular_data_rollover_system.json +0 -258
- planar/rules/test_data/clinical_trial_eligibility_screener.json +0 -437
- planar/rules/test_data/customer_lifetime_value.json +0 -143
- planar/rules/test_data/import_duties_calculator.json +0 -289
- planar/rules/test_data/insurance_prior_authorization.json +0 -443
- planar/rules/test_data/online_check_in_eligibility_system.json +0 -254
- planar/rules/test_data/order_consolidation_system.json +0 -375
- planar/rules/test_data/portfolio_risk_monitor.json +0 -471
- planar/rules/test_data/supply_chain_risk.json +0 -253
- planar/rules/test_data/warehouse_cross_docking.json +0 -237
- planar/rules/test_rules.py +0 -1494
- planar/security/tests/test_auth_middleware.py +0 -162
- planar/security/tests/test_authorization_context.py +0 -78
- planar/security/tests/test_cedar_basics.py +0 -41
- planar/security/tests/test_cedar_policies.py +0 -158
- planar/security/tests/test_jwt_principal_context.py +0 -179
- planar/test_app.py +0 -142
- planar/test_cli.py +0 -394
- planar/test_config.py +0 -515
- planar/test_object_config.py +0 -527
- planar/test_object_registry.py +0 -14
- planar/test_sqlalchemy.py +0 -193
- planar/test_utils.py +0 -105
- planar/testing/test_memory_storage.py +0 -143
- planar/workflows/test_concurrency_detection.py +0 -120
- planar/workflows/test_lock_timeout.py +0 -140
- planar/workflows/test_serialization.py +0 -1203
- planar/workflows/test_suspend_deserialization.py +0 -231
- planar/workflows/test_workflow.py +0 -2005
- planar-0.10.0.dist-info/METADATA +0 -323
- {planar-0.10.0.dist-info → planar-0.12.0.dist-info}/WHEEL +0 -0
- {planar-0.10.0.dist-info → planar-0.12.0.dist-info}/entry_points.txt +0 -0
planar/test_object_config.py
DELETED
@@ -1,527 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Tests for object configuration schema validation functionality.
|
3
|
-
|
4
|
-
Also Tests for the object configuration promotion logic.
|
5
|
-
|
6
|
-
This module consolidates tests for the promote_config functionality
|
7
|
-
used by the object_config_router, covering both agent and rule configurations.
|
8
|
-
"""
|
9
|
-
|
10
|
-
from uuid import uuid4
|
11
|
-
|
12
|
-
import pytest
|
13
|
-
from pydantic import BaseModel
|
14
|
-
from sqlmodel import select
|
15
|
-
from sqlmodel.ext.asyncio.session import AsyncSession
|
16
|
-
|
17
|
-
from planar.ai.agent import Agent
|
18
|
-
from planar.ai.agent_utils import agent_configuration
|
19
|
-
from planar.ai.models import AgentConfig
|
20
|
-
from planar.object_config import (
|
21
|
-
DEFAULT_UUID,
|
22
|
-
ConfigNotFoundError,
|
23
|
-
ConfigurableObjectType,
|
24
|
-
ObjectConfiguration,
|
25
|
-
ObjectConfigurationIO,
|
26
|
-
)
|
27
|
-
from planar.object_registry import ObjectRegistry
|
28
|
-
from planar.rules.models import Rule, RuleEngineConfig, create_jdm_graph
|
29
|
-
from planar.rules.rule_configuration import rule_configuration
|
30
|
-
|
31
|
-
|
32
|
-
@pytest.fixture
|
33
|
-
def rule_definition():
|
34
|
-
class RuleInputOutput(BaseModel):
|
35
|
-
test: str
|
36
|
-
|
37
|
-
return Rule(
|
38
|
-
name="test_rule_promote_success",
|
39
|
-
description="Test rule for promoting configuration",
|
40
|
-
input=RuleInputOutput,
|
41
|
-
output=RuleInputOutput,
|
42
|
-
)
|
43
|
-
|
44
|
-
|
45
|
-
@pytest.fixture
|
46
|
-
def agent_definition():
|
47
|
-
class AgentInputOutput(BaseModel):
|
48
|
-
test: str
|
49
|
-
|
50
|
-
return Agent(
|
51
|
-
name="test_agent_promote_success",
|
52
|
-
system_prompt="Test agent for promoting configuration",
|
53
|
-
user_prompt="Test agent for promoting configuration",
|
54
|
-
model="gpt-4",
|
55
|
-
max_turns=1,
|
56
|
-
)
|
57
|
-
|
58
|
-
|
59
|
-
class ConfigV1(BaseModel):
|
60
|
-
"""Version 1 of a test configuration schema."""
|
61
|
-
|
62
|
-
name: str
|
63
|
-
value: int
|
64
|
-
|
65
|
-
|
66
|
-
@pytest.fixture
|
67
|
-
def config_io_v1():
|
68
|
-
"""Configuration IO for version 1 schema."""
|
69
|
-
return ObjectConfigurationIO(ConfigV1, ConfigurableObjectType.RULE)
|
70
|
-
|
71
|
-
|
72
|
-
async def test_schema_validation_success(session: AsyncSession, config_io_v1):
|
73
|
-
"""Test that valid configurations are loaded"""
|
74
|
-
# Write a valid V1 configuration
|
75
|
-
config_v1 = ConfigV1(name="test", value=42)
|
76
|
-
await config_io_v1.write_config("test_object", config_v1)
|
77
|
-
|
78
|
-
# Read it back with validation
|
79
|
-
result = await config_io_v1._read_configs("test_object")
|
80
|
-
|
81
|
-
assert len(result) == 1
|
82
|
-
assert result[0].data.name == "test"
|
83
|
-
assert result[0].data.value == 42
|
84
|
-
|
85
|
-
|
86
|
-
async def test_no_config_returns_empty_list(session: AsyncSession, config_io_v1):
|
87
|
-
"""Test that non-existent configurations return empty list"""
|
88
|
-
result = await config_io_v1._read_configs("nonexistent_object")
|
89
|
-
|
90
|
-
assert len(result) == 0
|
91
|
-
|
92
|
-
|
93
|
-
async def test_multiple_versions_ordered_by_version_desc(
|
94
|
-
session: AsyncSession, config_io_v1
|
95
|
-
):
|
96
|
-
"""Test that multiple configurations are returned ordered by version descending."""
|
97
|
-
# Write multiple configurations
|
98
|
-
config_v1_1 = ConfigV1(name="test1", value=1)
|
99
|
-
config_v1_2 = ConfigV1(name="test2", value=2)
|
100
|
-
config_v1_3 = ConfigV1(name="test3", value=3)
|
101
|
-
|
102
|
-
await config_io_v1.write_config("test_object", config_v1_1)
|
103
|
-
await config_io_v1.write_config("test_object", config_v1_2)
|
104
|
-
await config_io_v1.write_config("test_object", config_v1_3)
|
105
|
-
|
106
|
-
# Read all configurations
|
107
|
-
result = await config_io_v1._read_configs("test_object")
|
108
|
-
|
109
|
-
# Should have 3 configurations, ordered by version descending
|
110
|
-
assert len(result) == 3
|
111
|
-
assert result[0].version == 3 # Latest version first
|
112
|
-
assert result[0].data.name == "test3"
|
113
|
-
assert result[1].version == 2
|
114
|
-
assert result[1].data.name == "test2"
|
115
|
-
assert result[2].version == 1 # Oldest version last
|
116
|
-
assert result[2].data.name == "test1"
|
117
|
-
|
118
|
-
|
119
|
-
@pytest.mark.asyncio
|
120
|
-
class TestPromoteConfigurationLogic:
|
121
|
-
"""Test the promote configuration logic for agents and rules."""
|
122
|
-
|
123
|
-
async def test_promote_rule_config_success(
|
124
|
-
self, session: AsyncSession, rule_definition: Rule
|
125
|
-
):
|
126
|
-
"""Test promoting a rule configuration successfully."""
|
127
|
-
ObjectRegistry.get_instance().register(rule_definition)
|
128
|
-
|
129
|
-
rule_config = RuleEngineConfig(jdm=create_jdm_graph(rule_definition))
|
130
|
-
config = await rule_configuration.write_config(
|
131
|
-
rule_definition.name, rule_config
|
132
|
-
)
|
133
|
-
|
134
|
-
await rule_configuration.promote_config(config.id)
|
135
|
-
|
136
|
-
configs = await rule_configuration.read_configs_with_default(
|
137
|
-
rule_definition.name, rule_definition.to_config()
|
138
|
-
)
|
139
|
-
|
140
|
-
assert len(configs) == 2
|
141
|
-
assert configs[0].active is True
|
142
|
-
assert configs[0].version == 1
|
143
|
-
|
144
|
-
# last config is default
|
145
|
-
assert configs[1].active is False
|
146
|
-
assert configs[1].version == 0
|
147
|
-
|
148
|
-
db_config = (
|
149
|
-
await session.exec(
|
150
|
-
select(ObjectConfiguration).where(ObjectConfiguration.id == config.id)
|
151
|
-
)
|
152
|
-
).first()
|
153
|
-
assert db_config is not None
|
154
|
-
assert db_config.active is True
|
155
|
-
|
156
|
-
async def test_promote_agent_config_success(
|
157
|
-
self, session: AsyncSession, agent_definition: Agent
|
158
|
-
):
|
159
|
-
"""Test promoting an agent configuration successfully."""
|
160
|
-
object_name = f"test_agent_promote_success_{uuid4().hex}"
|
161
|
-
agent_config = AgentConfig(
|
162
|
-
system_prompt="Sys", user_prompt="User", model="gpt-4", max_turns=1
|
163
|
-
)
|
164
|
-
config = await agent_configuration.write_config(object_name, agent_config)
|
165
|
-
|
166
|
-
await agent_configuration.promote_config(config.id)
|
167
|
-
|
168
|
-
configs = await agent_configuration.read_configs_with_default(
|
169
|
-
object_name, agent_definition.to_config()
|
170
|
-
)
|
171
|
-
|
172
|
-
assert len(configs) == 2
|
173
|
-
assert configs[0].active is True
|
174
|
-
assert configs[0].id == config.id
|
175
|
-
|
176
|
-
db_config = (
|
177
|
-
await session.exec(
|
178
|
-
select(ObjectConfiguration).where(ObjectConfiguration.id == config.id)
|
179
|
-
)
|
180
|
-
).first()
|
181
|
-
assert db_config is not None
|
182
|
-
assert db_config.active is True
|
183
|
-
|
184
|
-
async def test_promote_switches_active_rule_configs(
|
185
|
-
self, session: AsyncSession, rule_definition: Rule
|
186
|
-
):
|
187
|
-
"""Test that promoting a rule config switches active status correctly."""
|
188
|
-
ObjectRegistry.get_instance().register(rule_definition)
|
189
|
-
|
190
|
-
jdm_graph = create_jdm_graph(rule_definition)
|
191
|
-
|
192
|
-
config1_payload = RuleEngineConfig(jdm=jdm_graph)
|
193
|
-
config2_payload = RuleEngineConfig(jdm=jdm_graph)
|
194
|
-
|
195
|
-
config1 = await rule_configuration.write_config(
|
196
|
-
rule_definition.name, config1_payload
|
197
|
-
)
|
198
|
-
config2 = await rule_configuration.write_config(
|
199
|
-
rule_definition.name, config2_payload
|
200
|
-
)
|
201
|
-
|
202
|
-
await rule_configuration.promote_config(config1.id)
|
203
|
-
await rule_configuration.promote_config(config2.id)
|
204
|
-
|
205
|
-
configs = await rule_configuration.read_configs_with_default(
|
206
|
-
rule_definition.name, rule_definition.to_config()
|
207
|
-
)
|
208
|
-
|
209
|
-
assert len(configs) == 3
|
210
|
-
active_configs = [c for c in configs if c.active]
|
211
|
-
inactive_configs = [c for c in configs if not c.active]
|
212
|
-
|
213
|
-
assert len(active_configs) == 1
|
214
|
-
assert active_configs[0].id == config2.id
|
215
|
-
assert len(inactive_configs) == 2
|
216
|
-
assert inactive_configs[0].id == config1.id
|
217
|
-
assert inactive_configs[1].id == DEFAULT_UUID
|
218
|
-
|
219
|
-
db_config1 = (
|
220
|
-
await session.exec(
|
221
|
-
select(ObjectConfiguration).where(ObjectConfiguration.id == config1.id)
|
222
|
-
)
|
223
|
-
).first()
|
224
|
-
db_config2 = (
|
225
|
-
await session.exec(
|
226
|
-
select(ObjectConfiguration).where(ObjectConfiguration.id == config2.id)
|
227
|
-
)
|
228
|
-
).first()
|
229
|
-
assert db_config1 is not None and db_config1.active is False
|
230
|
-
assert db_config2 is not None and db_config2.active is True
|
231
|
-
|
232
|
-
async def test_promote_switches_active_agent_configs(
|
233
|
-
self, session: AsyncSession, agent_definition: Agent
|
234
|
-
):
|
235
|
-
"""Test that promoting an agent config switches active status correctly."""
|
236
|
-
object_name = f"test_agent_switches_active_{uuid4().hex}"
|
237
|
-
config1_payload = AgentConfig(
|
238
|
-
system_prompt="S1", user_prompt="U1", model="m1", max_turns=1
|
239
|
-
)
|
240
|
-
config2_payload = AgentConfig(
|
241
|
-
system_prompt="S2", user_prompt="U2", model="m2", max_turns=2
|
242
|
-
)
|
243
|
-
|
244
|
-
config1 = await agent_configuration.write_config(object_name, config1_payload)
|
245
|
-
config2 = await agent_configuration.write_config(object_name, config2_payload)
|
246
|
-
|
247
|
-
await agent_configuration.promote_config(config1.id)
|
248
|
-
await agent_configuration.promote_config(config2.id)
|
249
|
-
|
250
|
-
configs = await agent_configuration.read_configs_with_default(
|
251
|
-
object_name, agent_definition.to_config()
|
252
|
-
)
|
253
|
-
|
254
|
-
assert len(configs) == 3
|
255
|
-
active_configs = [c for c in configs if c.active]
|
256
|
-
inactive_configs = [c for c in configs if not c.active]
|
257
|
-
|
258
|
-
assert len(active_configs) == 1
|
259
|
-
assert active_configs[0].id == config2.id
|
260
|
-
assert len(inactive_configs) == 2
|
261
|
-
|
262
|
-
db_config1 = (
|
263
|
-
await session.exec(
|
264
|
-
select(ObjectConfiguration).where(ObjectConfiguration.id == config1.id)
|
265
|
-
)
|
266
|
-
).first()
|
267
|
-
db_config2 = (
|
268
|
-
await session.exec(
|
269
|
-
select(ObjectConfiguration).where(ObjectConfiguration.id == config2.id)
|
270
|
-
)
|
271
|
-
).first()
|
272
|
-
assert db_config1 is not None and db_config1.active is False
|
273
|
-
assert db_config2 is not None and db_config2.active is True
|
274
|
-
|
275
|
-
async def test_promote_default_rule_config_reverts(
|
276
|
-
self, session: AsyncSession, rule_definition: Rule
|
277
|
-
):
|
278
|
-
"""Test promoting default rule config (UUID all zeros) reverts to original."""
|
279
|
-
ObjectRegistry.get_instance().register(rule_definition)
|
280
|
-
rule_config = RuleEngineConfig(jdm=create_jdm_graph(rule_definition))
|
281
|
-
config = await rule_configuration.write_config(
|
282
|
-
rule_definition.name, rule_config
|
283
|
-
)
|
284
|
-
|
285
|
-
await rule_configuration.promote_config(config.id)
|
286
|
-
db_config_promoted = (
|
287
|
-
await session.exec(
|
288
|
-
select(ObjectConfiguration).where(ObjectConfiguration.id == config.id)
|
289
|
-
)
|
290
|
-
).first()
|
291
|
-
assert db_config_promoted is not None and db_config_promoted.active is True
|
292
|
-
|
293
|
-
await session.commit()
|
294
|
-
|
295
|
-
await rule_configuration.promote_config(
|
296
|
-
DEFAULT_UUID, object_name=rule_definition.name
|
297
|
-
)
|
298
|
-
|
299
|
-
configs = await rule_configuration.read_configs_with_default(
|
300
|
-
rule_definition.name, rule_definition.to_config()
|
301
|
-
)
|
302
|
-
|
303
|
-
assert len(configs) == 2
|
304
|
-
assert configs[0].version == 1
|
305
|
-
assert configs[1].version == 0
|
306
|
-
|
307
|
-
assert configs[0].active is False
|
308
|
-
assert configs[1].active is True
|
309
|
-
|
310
|
-
db_config_reverted = (
|
311
|
-
await session.exec(
|
312
|
-
select(ObjectConfiguration).where(ObjectConfiguration.id == config.id)
|
313
|
-
)
|
314
|
-
).first()
|
315
|
-
assert db_config_reverted is not None and db_config_reverted.active is False
|
316
|
-
|
317
|
-
async def test_promote_default_agent_config_reverts(
|
318
|
-
self, session: AsyncSession, agent_definition: Agent
|
319
|
-
):
|
320
|
-
"""Test promoting default agent config (UUID all zeros) reverts."""
|
321
|
-
object_name = f"test_agent_revert_default_{uuid4().hex}"
|
322
|
-
agent_config = AgentConfig(
|
323
|
-
system_prompt="S", user_prompt="U", model="m", max_turns=1
|
324
|
-
)
|
325
|
-
config = await agent_configuration.write_config(object_name, agent_config)
|
326
|
-
|
327
|
-
await agent_configuration.promote_config(config.id)
|
328
|
-
db_config_promoted = (
|
329
|
-
await session.exec(
|
330
|
-
select(ObjectConfiguration).where(ObjectConfiguration.id == config.id)
|
331
|
-
)
|
332
|
-
).first()
|
333
|
-
assert db_config_promoted is not None and db_config_promoted.active is True
|
334
|
-
|
335
|
-
await session.commit()
|
336
|
-
|
337
|
-
await agent_configuration.promote_config(DEFAULT_UUID, object_name=object_name)
|
338
|
-
|
339
|
-
configs = await agent_configuration.read_configs_with_default(
|
340
|
-
object_name, agent_definition.to_config()
|
341
|
-
)
|
342
|
-
|
343
|
-
assert len(configs) == 2
|
344
|
-
|
345
|
-
assert configs[0].version == 1
|
346
|
-
assert configs[1].version == 0
|
347
|
-
|
348
|
-
assert configs[0].active is False
|
349
|
-
assert configs[1].active is True
|
350
|
-
|
351
|
-
db_config_reverted = (
|
352
|
-
await session.exec(
|
353
|
-
select(ObjectConfiguration).where(ObjectConfiguration.id == config.id)
|
354
|
-
)
|
355
|
-
).first()
|
356
|
-
assert db_config_reverted is not None and db_config_reverted.active is False
|
357
|
-
|
358
|
-
async def test_promote_nonexistent_config_raises_error(self, session: AsyncSession):
|
359
|
-
"""Test promoting a non-existent config ID raises ConfigNotFoundError."""
|
360
|
-
nonexistent_id = uuid4()
|
361
|
-
with pytest.raises(ConfigNotFoundError) as excinfo_rule:
|
362
|
-
await rule_configuration.promote_config(nonexistent_id)
|
363
|
-
assert excinfo_rule.value.invalid_id == nonexistent_id
|
364
|
-
assert excinfo_rule.value.object_type == ConfigurableObjectType.RULE
|
365
|
-
|
366
|
-
with pytest.raises(ConfigNotFoundError) as excinfo_agent:
|
367
|
-
await agent_configuration.promote_config(nonexistent_id)
|
368
|
-
assert excinfo_agent.value.invalid_id == nonexistent_id
|
369
|
-
assert excinfo_agent.value.object_type == ConfigurableObjectType.AGENT
|
370
|
-
|
371
|
-
async def test_promote_default_config_requires_object_name(
|
372
|
-
self, session: AsyncSession
|
373
|
-
):
|
374
|
-
"""Test promoting default UUID requires object_name parameter."""
|
375
|
-
with pytest.raises(
|
376
|
-
ValueError,
|
377
|
-
match="object_name is required when reverting to default configuration",
|
378
|
-
):
|
379
|
-
await rule_configuration.promote_config(DEFAULT_UUID)
|
380
|
-
with pytest.raises(
|
381
|
-
ValueError,
|
382
|
-
match="object_name is required when reverting to default configuration",
|
383
|
-
):
|
384
|
-
await agent_configuration.promote_config(DEFAULT_UUID)
|
385
|
-
|
386
|
-
async def test_default_config_is_active_when_no_other_configs_present(
|
387
|
-
self, session: AsyncSession, rule_definition: Rule, agent_definition: Agent
|
388
|
-
):
|
389
|
-
"""Test default config is active when no custom configs exist for the object_name."""
|
390
|
-
object_name_rule = f"nonexistent_rule_for_default_{uuid4().hex}"
|
391
|
-
object_name_agent = f"nonexistent_agent_for_default_{uuid4().hex}"
|
392
|
-
|
393
|
-
configs = await rule_configuration.read_configs_with_default(
|
394
|
-
object_name_rule, rule_definition.to_config()
|
395
|
-
)
|
396
|
-
assert len(configs) == 1
|
397
|
-
assert configs[0].version == 0
|
398
|
-
assert configs[0].active is True
|
399
|
-
|
400
|
-
await agent_configuration.promote_config(
|
401
|
-
DEFAULT_UUID, object_name=object_name_agent
|
402
|
-
)
|
403
|
-
configs = await agent_configuration.read_configs_with_default(
|
404
|
-
object_name_agent, agent_definition.to_config()
|
405
|
-
)
|
406
|
-
assert len(configs) == 1
|
407
|
-
assert configs[0].version == 0
|
408
|
-
assert configs[0].active is True
|
409
|
-
|
410
|
-
async def test_promote_returns_all_configs_ordered(
|
411
|
-
self, session: AsyncSession, agent_definition: Agent
|
412
|
-
):
|
413
|
-
"""Test that promote_config returns all configurations for the object_name, ordered by version."""
|
414
|
-
object_name = f"test_agent_promote_all_ordered_{uuid4().hex}"
|
415
|
-
created_configs = []
|
416
|
-
for i in range(3):
|
417
|
-
config = AgentConfig(
|
418
|
-
system_prompt=f"Config {i}",
|
419
|
-
user_prompt=f"UP {i}",
|
420
|
-
model="m",
|
421
|
-
max_turns=i + 1,
|
422
|
-
)
|
423
|
-
cfg = await agent_configuration.write_config(object_name, config)
|
424
|
-
created_configs.append(cfg)
|
425
|
-
|
426
|
-
# Promote the middle config (chronologically, so it's version 2, index 1 if created_configs is in creation order)
|
427
|
-
# Assuming versions are 1, 2, 3. Created_configs[1] is version 2.
|
428
|
-
# The versions will be 3, 2, 1 in the database after 3 writes.
|
429
|
-
# So created_configs[0] (first written) would be version 1.
|
430
|
-
# created_configs[1] (second written) would be version 2.
|
431
|
-
# created_configs[2] (third written) would be version 3.
|
432
|
-
|
433
|
-
# Let's fetch all configs to be sure about IDs and versions before promoting
|
434
|
-
all_configs_before_promote = (
|
435
|
-
await agent_configuration.read_configs_with_default(
|
436
|
-
object_name, agent_definition.to_config()
|
437
|
-
)
|
438
|
-
)
|
439
|
-
ids_by_version_desc = [
|
440
|
-
c.id for c in all_configs_before_promote
|
441
|
-
] # Should be [id_v3, id_v2, id_v1, id_default]
|
442
|
-
|
443
|
-
# Verify that the default config is active by default
|
444
|
-
default_config = all_configs_before_promote[-1]
|
445
|
-
assert default_config.active is True, (
|
446
|
-
"Default config should be active by default"
|
447
|
-
)
|
448
|
-
|
449
|
-
# Promote the one that corresponds to original version 2 (second created)
|
450
|
-
# This ID would be ids_by_version_desc[1]
|
451
|
-
config_to_promote_id = ids_by_version_desc[1]
|
452
|
-
|
453
|
-
await agent_configuration.promote_config(config_to_promote_id)
|
454
|
-
|
455
|
-
all_configs_after_promote = await agent_configuration.read_configs_with_default(
|
456
|
-
object_name, agent_definition.to_config()
|
457
|
-
)
|
458
|
-
|
459
|
-
assert len(all_configs_after_promote) == 4
|
460
|
-
|
461
|
-
versions = [config.version for config in all_configs_after_promote]
|
462
|
-
assert versions == sorted(versions, reverse=True), (
|
463
|
-
"Configs should be sorted by version descending"
|
464
|
-
)
|
465
|
-
|
466
|
-
active_configs = [
|
467
|
-
config for config in all_configs_after_promote if config.active
|
468
|
-
]
|
469
|
-
assert len(active_configs) == 1
|
470
|
-
assert active_configs[0].id == config_to_promote_id
|
471
|
-
|
472
|
-
async def test_promote_multiple_configs_only_one_active_rule(
|
473
|
-
self, session: AsyncSession, rule_definition: Rule
|
474
|
-
):
|
475
|
-
"""Test that when multiple rule configs exist, only the promoted one is active."""
|
476
|
-
ObjectRegistry.get_instance().register(rule_definition)
|
477
|
-
|
478
|
-
config_ids = []
|
479
|
-
for i in range(3):
|
480
|
-
rule_config = RuleEngineConfig(jdm=create_jdm_graph(rule_definition))
|
481
|
-
cfg = await rule_configuration.write_config(
|
482
|
-
rule_definition.name, rule_config
|
483
|
-
)
|
484
|
-
config_ids.append(cfg.id)
|
485
|
-
|
486
|
-
# Config_ids are in order of creation: [id_v1, id_v2, id_v3]
|
487
|
-
# After all writes, read_configs would return them ordered [v3, v2, v1]
|
488
|
-
# Let's promote the middle one created (which would be version 2)
|
489
|
-
# To get its ID robustly, let's re-fetch and pick based on version or order.
|
490
|
-
all_cfgs = await rule_configuration._read_configs(rule_definition.name)
|
491
|
-
|
492
|
-
# all_cfgs.configs is sorted by version desc (e.g. [v3, v2, v1])
|
493
|
-
# We want to promote the one with original version 2. This is all_cfgs.configs[1]
|
494
|
-
id_to_promote = all_cfgs[1].id
|
495
|
-
|
496
|
-
await rule_configuration.promote_config(id_to_promote)
|
497
|
-
|
498
|
-
configs = await rule_configuration.read_configs_with_default(
|
499
|
-
rule_definition.name, rule_definition.to_config()
|
500
|
-
)
|
501
|
-
|
502
|
-
assert len(configs) == 4
|
503
|
-
active_found = [c for c in configs if c.active]
|
504
|
-
assert len(active_found) == 1
|
505
|
-
assert active_found[0].id == id_to_promote
|
506
|
-
|
507
|
-
# Verify in database: iterate through all initially created config IDs
|
508
|
-
# and check their active status. Only id_to_promote should be active.
|
509
|
-
# config_ids was populated in order of creation: [id_for_v1, id_for_v2, id_for_v3]
|
510
|
-
# id_to_promote corresponds to the one with version 2 (the second one created).
|
511
|
-
for created_id in config_ids:
|
512
|
-
db_config = (
|
513
|
-
await session.exec(
|
514
|
-
select(ObjectConfiguration).where(
|
515
|
-
ObjectConfiguration.id == created_id
|
516
|
-
)
|
517
|
-
)
|
518
|
-
).first()
|
519
|
-
assert db_config is not None, f"Config with ID {created_id} not found in DB"
|
520
|
-
if created_id == id_to_promote:
|
521
|
-
assert db_config.active is True, (
|
522
|
-
f"Promoted config {created_id} should be active"
|
523
|
-
)
|
524
|
-
else:
|
525
|
-
assert db_config.active is False, (
|
526
|
-
f"Non-promoted config {created_id} should be inactive"
|
527
|
-
)
|
planar/test_object_registry.py
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
from planar.registry_items import create_pydantic_model_for_workflow
|
2
|
-
from planar.workflows.decorators import workflow
|
3
|
-
|
4
|
-
|
5
|
-
@workflow()
|
6
|
-
async def sample_workflow(foo: int, bar: str = "baz"):
|
7
|
-
pass
|
8
|
-
|
9
|
-
|
10
|
-
def test_create_pydantic_model_for_workflow_strips_module_name():
|
11
|
-
model_cls = create_pydantic_model_for_workflow(sample_workflow)
|
12
|
-
assert model_cls.__name__ == "SampleWorkflowStartRequest"
|
13
|
-
assert "foo" in model_cls.model_fields
|
14
|
-
assert "bar" in model_cls.model_fields
|