planar 0.5.0__py3-none-any.whl → 0.7.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/_version.py +1 -1
- planar/ai/agent.py +67 -30
- planar/ai/pydantic_ai.py +570 -0
- planar/ai/pydantic_ai_agent.py +329 -0
- planar/ai/test_agent.py +2 -2
- planar/app.py +64 -20
- planar/cli.py +39 -27
- planar/config.py +45 -36
- planar/db/db.py +2 -1
- planar/files/storage/azure_blob.py +343 -0
- planar/files/storage/base.py +7 -0
- planar/files/storage/config.py +70 -7
- planar/files/storage/s3.py +6 -6
- planar/files/storage/test_azure_blob.py +435 -0
- planar/logging/formatter.py +17 -4
- planar/logging/test_formatter.py +327 -0
- planar/registry_items.py +2 -1
- planar/routers/agents_router.py +3 -1
- planar/routers/files.py +11 -2
- planar/routers/models.py +14 -1
- planar/routers/test_files_router.py +49 -0
- planar/routers/test_routes_security.py +5 -7
- planar/routers/test_workflow_router.py +270 -3
- planar/routers/workflow.py +95 -36
- planar/rules/models.py +36 -39
- planar/rules/test_data/account_dormancy_management.json +223 -0
- planar/rules/test_data/airline_loyalty_points_calculator.json +262 -0
- planar/rules/test_data/applicant_risk_assessment.json +435 -0
- planar/rules/test_data/booking_fraud_detection.json +407 -0
- planar/rules/test_data/cellular_data_rollover_system.json +258 -0
- planar/rules/test_data/clinical_trial_eligibility_screener.json +437 -0
- planar/rules/test_data/customer_lifetime_value.json +143 -0
- planar/rules/test_data/import_duties_calculator.json +289 -0
- planar/rules/test_data/insurance_prior_authorization.json +443 -0
- planar/rules/test_data/online_check_in_eligibility_system.json +254 -0
- planar/rules/test_data/order_consolidation_system.json +375 -0
- planar/rules/test_data/portfolio_risk_monitor.json +471 -0
- planar/rules/test_data/supply_chain_risk.json +253 -0
- planar/rules/test_data/warehouse_cross_docking.json +237 -0
- planar/rules/test_rules.py +750 -6
- planar/scaffold_templates/planar.dev.yaml.j2 +6 -6
- planar/scaffold_templates/planar.prod.yaml.j2 +9 -5
- planar/scaffold_templates/pyproject.toml.j2 +1 -1
- planar/security/auth_context.py +21 -0
- planar/security/{jwt_middleware.py → auth_middleware.py} +70 -17
- planar/security/authorization.py +9 -15
- planar/security/tests/test_auth_middleware.py +162 -0
- planar/sse/proxy.py +4 -9
- planar/test_app.py +92 -1
- planar/test_cli.py +81 -59
- planar/test_config.py +17 -14
- planar/testing/fixtures.py +325 -0
- planar/testing/planar_test_client.py +5 -2
- planar/utils.py +41 -1
- planar/workflows/execution.py +1 -1
- planar/workflows/orchestrator.py +5 -0
- planar/workflows/serialization.py +12 -6
- planar/workflows/step_core.py +3 -1
- planar/workflows/test_serialization.py +9 -1
- {planar-0.5.0.dist-info → planar-0.7.0.dist-info}/METADATA +30 -5
- planar-0.7.0.dist-info/RECORD +169 -0
- planar/.__init__.py.un~ +0 -0
- planar/._version.py.un~ +0 -0
- planar/.app.py.un~ +0 -0
- planar/.cli.py.un~ +0 -0
- planar/.config.py.un~ +0 -0
- planar/.context.py.un~ +0 -0
- planar/.db.py.un~ +0 -0
- planar/.di.py.un~ +0 -0
- planar/.engine.py.un~ +0 -0
- planar/.files.py.un~ +0 -0
- planar/.log_context.py.un~ +0 -0
- planar/.log_metadata.py.un~ +0 -0
- planar/.logging.py.un~ +0 -0
- planar/.object_registry.py.un~ +0 -0
- planar/.otel.py.un~ +0 -0
- planar/.server.py.un~ +0 -0
- planar/.session.py.un~ +0 -0
- planar/.sqlalchemy.py.un~ +0 -0
- planar/.task_local.py.un~ +0 -0
- planar/.test_app.py.un~ +0 -0
- planar/.test_config.py.un~ +0 -0
- planar/.test_object_config.py.un~ +0 -0
- planar/.test_sqlalchemy.py.un~ +0 -0
- planar/.test_utils.py.un~ +0 -0
- planar/.util.py.un~ +0 -0
- planar/.utils.py.un~ +0 -0
- planar/ai/.__init__.py.un~ +0 -0
- planar/ai/._models.py.un~ +0 -0
- planar/ai/.agent.py.un~ +0 -0
- planar/ai/.agent_utils.py.un~ +0 -0
- planar/ai/.events.py.un~ +0 -0
- planar/ai/.files.py.un~ +0 -0
- planar/ai/.models.py.un~ +0 -0
- planar/ai/.providers.py.un~ +0 -0
- planar/ai/.pydantic_ai.py.un~ +0 -0
- planar/ai/.pydantic_ai_agent.py.un~ +0 -0
- planar/ai/.pydantic_ai_provider.py.un~ +0 -0
- planar/ai/.step.py.un~ +0 -0
- planar/ai/.test_agent.py.un~ +0 -0
- planar/ai/.test_agent_serialization.py.un~ +0 -0
- planar/ai/.test_providers.py.un~ +0 -0
- planar/ai/.utils.py.un~ +0 -0
- planar/db/.db.py.un~ +0 -0
- planar/files/.config.py.un~ +0 -0
- planar/files/.local.py.un~ +0 -0
- planar/files/.local_filesystem.py.un~ +0 -0
- planar/files/.model.py.un~ +0 -0
- planar/files/.models.py.un~ +0 -0
- planar/files/.s3.py.un~ +0 -0
- planar/files/.storage.py.un~ +0 -0
- planar/files/.test_files.py.un~ +0 -0
- planar/files/storage/.__init__.py.un~ +0 -0
- planar/files/storage/.base.py.un~ +0 -0
- planar/files/storage/.config.py.un~ +0 -0
- planar/files/storage/.context.py.un~ +0 -0
- planar/files/storage/.local_directory.py.un~ +0 -0
- planar/files/storage/.test_local_directory.py.un~ +0 -0
- planar/files/storage/.test_s3.py.un~ +0 -0
- planar/human/.human.py.un~ +0 -0
- planar/human/.test_human.py.un~ +0 -0
- planar/logging/.__init__.py.un~ +0 -0
- planar/logging/.attributes.py.un~ +0 -0
- planar/logging/.formatter.py.un~ +0 -0
- planar/logging/.logger.py.un~ +0 -0
- planar/logging/.otel.py.un~ +0 -0
- planar/logging/.tracer.py.un~ +0 -0
- planar/modeling/.mixin.py.un~ +0 -0
- planar/modeling/.storage.py.un~ +0 -0
- planar/modeling/orm/.planar_base_model.py.un~ +0 -0
- planar/object_config/.object_config.py.un~ +0 -0
- planar/routers/.__init__.py.un~ +0 -0
- planar/routers/.agents_router.py.un~ +0 -0
- planar/routers/.crud.py.un~ +0 -0
- planar/routers/.decision.py.un~ +0 -0
- planar/routers/.event.py.un~ +0 -0
- planar/routers/.file_attachment.py.un~ +0 -0
- planar/routers/.files.py.un~ +0 -0
- planar/routers/.files_router.py.un~ +0 -0
- planar/routers/.human.py.un~ +0 -0
- planar/routers/.info.py.un~ +0 -0
- planar/routers/.models.py.un~ +0 -0
- planar/routers/.object_config_router.py.un~ +0 -0
- planar/routers/.rule.py.un~ +0 -0
- planar/routers/.test_object_config_router.py.un~ +0 -0
- planar/routers/.test_workflow_router.py.un~ +0 -0
- planar/routers/.workflow.py.un~ +0 -0
- planar/rules/.decorator.py.un~ +0 -0
- planar/rules/.runner.py.un~ +0 -0
- planar/rules/.test_rules.py.un~ +0 -0
- planar/security/.jwt_middleware.py.un~ +0 -0
- planar/sse/.constants.py.un~ +0 -0
- planar/sse/.example.html.un~ +0 -0
- planar/sse/.hub.py.un~ +0 -0
- planar/sse/.model.py.un~ +0 -0
- planar/sse/.proxy.py.un~ +0 -0
- planar/testing/.client.py.un~ +0 -0
- planar/testing/.memory_storage.py.un~ +0 -0
- planar/testing/.planar_test_client.py.un~ +0 -0
- planar/testing/.predictable_tracer.py.un~ +0 -0
- planar/testing/.synchronizable_tracer.py.un~ +0 -0
- planar/testing/.test_memory_storage.py.un~ +0 -0
- planar/testing/.workflow_observer.py.un~ +0 -0
- planar/workflows/.__init__.py.un~ +0 -0
- planar/workflows/.builtin_steps.py.un~ +0 -0
- planar/workflows/.concurrency_tracing.py.un~ +0 -0
- planar/workflows/.context.py.un~ +0 -0
- planar/workflows/.contrib.py.un~ +0 -0
- planar/workflows/.decorators.py.un~ +0 -0
- planar/workflows/.durable_test.py.un~ +0 -0
- planar/workflows/.errors.py.un~ +0 -0
- planar/workflows/.events.py.un~ +0 -0
- planar/workflows/.exceptions.py.un~ +0 -0
- planar/workflows/.execution.py.un~ +0 -0
- planar/workflows/.human.py.un~ +0 -0
- planar/workflows/.lock.py.un~ +0 -0
- planar/workflows/.misc.py.un~ +0 -0
- planar/workflows/.model.py.un~ +0 -0
- planar/workflows/.models.py.un~ +0 -0
- planar/workflows/.notifications.py.un~ +0 -0
- planar/workflows/.orchestrator.py.un~ +0 -0
- planar/workflows/.runtime.py.un~ +0 -0
- planar/workflows/.serialization.py.un~ +0 -0
- planar/workflows/.step.py.un~ +0 -0
- planar/workflows/.step_core.py.un~ +0 -0
- planar/workflows/.sub_workflow_runner.py.un~ +0 -0
- planar/workflows/.sub_workflow_scheduler.py.un~ +0 -0
- planar/workflows/.test_concurrency.py.un~ +0 -0
- planar/workflows/.test_concurrency_detection.py.un~ +0 -0
- planar/workflows/.test_human.py.un~ +0 -0
- planar/workflows/.test_lock_timeout.py.un~ +0 -0
- planar/workflows/.test_orchestrator.py.un~ +0 -0
- planar/workflows/.test_race_conditions.py.un~ +0 -0
- planar/workflows/.test_serialization.py.un~ +0 -0
- planar/workflows/.test_suspend_deserialization.py.un~ +0 -0
- planar/workflows/.test_workflow.py.un~ +0 -0
- planar/workflows/.tracing.py.un~ +0 -0
- planar/workflows/.types.py.un~ +0 -0
- planar/workflows/.util.py.un~ +0 -0
- planar/workflows/.utils.py.un~ +0 -0
- planar/workflows/.workflow.py.un~ +0 -0
- planar/workflows/.workflow_wrapper.py.un~ +0 -0
- planar/workflows/.wrappers.py.un~ +0 -0
- planar-0.5.0.dist-info/RECORD +0 -289
- {planar-0.5.0.dist-info → planar-0.7.0.dist-info}/WHEEL +0 -0
- {planar-0.5.0.dist-info → planar-0.7.0.dist-info}/entry_points.txt +0 -0
planar/_version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
VERSION = "0.
|
1
|
+
VERSION = "0.7.0"
|
planar/ai/agent.py
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import abc
|
3
4
|
import inspect
|
4
5
|
from dataclasses import dataclass, field
|
5
6
|
from typing import (
|
6
7
|
Any,
|
7
8
|
Callable,
|
9
|
+
Coroutine,
|
8
10
|
Dict,
|
9
11
|
List,
|
10
12
|
Type,
|
@@ -37,7 +39,7 @@ from planar.ai.models import (
|
|
37
39
|
from planar.ai.providers import Anthropic, Gemini, Model, OpenAI
|
38
40
|
from planar.logging import get_logger
|
39
41
|
from planar.modeling.field_helpers import JsonSchema
|
40
|
-
from planar.utils import utc_now
|
42
|
+
from planar.utils import P, R, T, U, utc_now
|
41
43
|
from planar.workflows import as_step
|
42
44
|
from planar.workflows.models import StepType
|
43
45
|
|
@@ -65,11 +67,11 @@ def _parse_model_string(model_str: str) -> Model:
|
|
65
67
|
|
66
68
|
|
67
69
|
@dataclass
|
68
|
-
class
|
70
|
+
class AgentBase[
|
69
71
|
# TODO: add `= str` default when we upgrade to 3.13
|
70
72
|
TInput: BaseModel | str,
|
71
73
|
TOutput: BaseModel | str,
|
72
|
-
]:
|
74
|
+
](abc.ABC):
|
73
75
|
"""An LLM-powered agent that can be called directly within workflows."""
|
74
76
|
|
75
77
|
name: str
|
@@ -77,10 +79,11 @@ class Agent[
|
|
77
79
|
output_type: Type[TOutput] | None = None
|
78
80
|
input_type: Type[TInput] | None = None
|
79
81
|
user_prompt: str = ""
|
80
|
-
model: Union[str, Model] = "openai:gpt-4.1"
|
81
82
|
tools: List[Callable] = field(default_factory=list)
|
82
83
|
max_turns: int = 2
|
83
84
|
model_parameters: Dict[str, Any] = field(default_factory=dict)
|
85
|
+
event_emitter: AgentEventEmitter | None = None
|
86
|
+
durable: bool = True
|
84
87
|
|
85
88
|
# TODO: move here to serialize to frontend
|
86
89
|
#
|
@@ -125,33 +128,37 @@ class Agent[
|
|
125
128
|
)
|
126
129
|
return self.output_type.model_json_schema()
|
127
130
|
|
128
|
-
def to_config(self) -> AgentConfig:
|
129
|
-
return AgentConfig(
|
130
|
-
system_prompt=self.system_prompt,
|
131
|
-
user_prompt=self.user_prompt,
|
132
|
-
model=str(self.model),
|
133
|
-
max_turns=self.max_turns,
|
134
|
-
model_parameters=self.model_parameters,
|
135
|
-
)
|
136
|
-
|
137
131
|
@overload
|
138
132
|
async def __call__(
|
139
|
-
self: "
|
133
|
+
self: "AgentBase[TInput, str]",
|
140
134
|
input_value: TInput,
|
141
|
-
event_emitter: AgentEventEmitter | None = None,
|
142
135
|
) -> AgentRunResult[str]: ...
|
143
136
|
|
144
137
|
@overload
|
145
138
|
async def __call__(
|
146
|
-
self: "
|
139
|
+
self: "AgentBase[TInput, TOutput]",
|
147
140
|
input_value: TInput,
|
148
|
-
event_emitter: AgentEventEmitter | None = None,
|
149
141
|
) -> AgentRunResult[TOutput]: ...
|
150
142
|
|
143
|
+
def as_step_if_durable(
|
144
|
+
self,
|
145
|
+
func: Callable[P, Coroutine[T, U, R]],
|
146
|
+
step_type: StepType,
|
147
|
+
display_name: str | None = None,
|
148
|
+
return_type: Type[R] | None = None,
|
149
|
+
) -> Callable[P, Coroutine[T, U, R]]:
|
150
|
+
if not self.durable:
|
151
|
+
return func
|
152
|
+
return as_step(
|
153
|
+
func,
|
154
|
+
step_type=step_type,
|
155
|
+
display_name=display_name or self.name,
|
156
|
+
return_type=return_type,
|
157
|
+
)
|
158
|
+
|
151
159
|
async def __call__(
|
152
160
|
self,
|
153
161
|
input_value: TInput,
|
154
|
-
event_emitter: AgentEventEmitter | None = None,
|
155
162
|
) -> AgentRunResult[Any]:
|
156
163
|
if self.input_type is not None and not isinstance(input_value, self.input_type):
|
157
164
|
raise ValueError(
|
@@ -165,31 +172,53 @@ class Agent[
|
|
165
172
|
)
|
166
173
|
|
167
174
|
if self.output_type is None:
|
168
|
-
run_step =
|
175
|
+
run_step = self.as_step_if_durable(
|
169
176
|
self.run_step,
|
170
177
|
step_type=StepType.AGENT,
|
171
178
|
display_name=self.name,
|
172
179
|
return_type=AgentRunResult[str],
|
173
180
|
)
|
174
181
|
else:
|
175
|
-
run_step =
|
182
|
+
run_step = self.as_step_if_durable(
|
176
183
|
self.run_step,
|
177
184
|
step_type=StepType.AGENT,
|
178
185
|
display_name=self.name,
|
179
186
|
return_type=AgentRunResult[self.output_type],
|
180
187
|
)
|
181
188
|
|
182
|
-
result = await run_step(
|
183
|
-
input_value=input_value,
|
184
|
-
event_emitter=event_emitter,
|
185
|
-
)
|
189
|
+
result = await run_step(input_value=input_value)
|
186
190
|
# Cast the result to ensure type compatibility
|
187
191
|
return cast(AgentRunResult[TOutput], result)
|
188
192
|
|
193
|
+
@abc.abstractmethod
|
194
|
+
async def run_step(
|
195
|
+
self,
|
196
|
+
input_value: TInput,
|
197
|
+
) -> AgentRunResult[TOutput]: ...
|
198
|
+
|
199
|
+
@abc.abstractmethod
|
200
|
+
def get_model_str(self) -> str: ...
|
201
|
+
|
202
|
+
def to_config(self) -> AgentConfig:
|
203
|
+
return AgentConfig(
|
204
|
+
system_prompt=self.system_prompt,
|
205
|
+
user_prompt=self.user_prompt,
|
206
|
+
model=self.get_model_str(),
|
207
|
+
max_turns=self.max_turns,
|
208
|
+
model_parameters=self.model_parameters,
|
209
|
+
)
|
210
|
+
|
211
|
+
|
212
|
+
@dataclass
|
213
|
+
class Agent[
|
214
|
+
TInput: BaseModel | str,
|
215
|
+
TOutput: BaseModel | str,
|
216
|
+
](AgentBase[TInput, TOutput]):
|
217
|
+
model: Union[str, Model] = "openai:gpt-4.1"
|
218
|
+
|
189
219
|
async def run_step(
|
190
220
|
self,
|
191
221
|
input_value: TInput,
|
192
|
-
event_emitter: AgentEventEmitter | None = None,
|
193
222
|
) -> AgentRunResult[TOutput]:
|
194
223
|
"""Execute the agent with the provided inputs.
|
195
224
|
|
@@ -200,8 +229,12 @@ class Agent[
|
|
200
229
|
Returns:
|
201
230
|
AgentRunResult containing the agent's response
|
202
231
|
"""
|
232
|
+
event_emitter = self.event_emitter
|
203
233
|
logger.debug(
|
204
|
-
"agent run_step called",
|
234
|
+
"agent run_step called",
|
235
|
+
agent_name=self.name,
|
236
|
+
input_type=type(input_value),
|
237
|
+
config=self.to_config(),
|
205
238
|
)
|
206
239
|
result = None
|
207
240
|
|
@@ -309,7 +342,7 @@ class Agent[
|
|
309
342
|
model=model.model_spec,
|
310
343
|
output_type=output_type_for_provider,
|
311
344
|
)
|
312
|
-
response = await
|
345
|
+
response = await self.as_step_if_durable(
|
313
346
|
model.provider_class.complete,
|
314
347
|
step_type=StepType.AGENT,
|
315
348
|
return_type=CompletionResponse[output_type_for_provider or str],
|
@@ -336,7 +369,7 @@ class Agent[
|
|
336
369
|
logger.debug("agent turn", agent_name=self.name, turns_left=turns_left)
|
337
370
|
|
338
371
|
# Get model response
|
339
|
-
response = await
|
372
|
+
response = await self.as_step_if_durable(
|
340
373
|
model.provider_class.complete,
|
341
374
|
step_type=StepType.AGENT,
|
342
375
|
return_type=CompletionResponse[output_type_for_provider or str],
|
@@ -397,7 +430,7 @@ class Agent[
|
|
397
430
|
)
|
398
431
|
else:
|
399
432
|
# Execute the tool with the provided arguments
|
400
|
-
tool_result = await
|
433
|
+
tool_result = await self.as_step_if_durable(
|
401
434
|
tool_fn,
|
402
435
|
step_type=StepType.TOOL_CALL,
|
403
436
|
)(**tool_call.arguments)
|
@@ -439,7 +472,8 @@ class Agent[
|
|
439
472
|
expected_type=self.output_type,
|
440
473
|
)
|
441
474
|
raise ValueError(
|
442
|
-
f"
|
475
|
+
f"Reached max turns without the expected result of type {self.output_type}. "
|
476
|
+
"You may need to increase the max_turns parameter or update the Agent instructions."
|
443
477
|
)
|
444
478
|
|
445
479
|
if event_emitter:
|
@@ -455,3 +489,6 @@ class Agent[
|
|
455
489
|
final_result_type=type(result),
|
456
490
|
)
|
457
491
|
return AgentRunResult[TOutput](output=cast(TOutput, result))
|
492
|
+
|
493
|
+
def get_model_str(self) -> str:
|
494
|
+
return str(self.model)
|