phronomy 0.26.0 → 0.28.0
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.
- checksums.yaml +4 -4
- data/.mutant.yml +9 -9
- data/CHANGELOG.md +92 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +13 -3
- data/VERIFY.sh +27 -27
- data/benchmark/bench_agent_invoke.rb +26 -47
- data/benchmark/bench_context_assembler.rb +17 -15
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/architecture/agent-chat-and-state-ownership.md +153 -0
- data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
- data/docs/architecture/agent-context.md +4 -2
- data/docs/architecture/agent-transition-ownership.md +13 -0
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +9 -3
- data/docs/architecture/context-management.md +31 -1
- data/docs/architecture/context-preparation-steps.md +90 -0
- data/docs/architecture/entry-action-and-team-wording.md +72 -0
- data/docs/architecture/execution-abstraction-review.md +88 -0
- data/docs/architecture/execution-boundaries.md +28 -0
- data/docs/architecture/execution-metadata-and-values.md +86 -0
- data/docs/architecture/generator-verifier-ownership.md +111 -0
- data/docs/architecture/multi-agent-handoff.md +11 -4
- data/docs/architecture/persistence-refactoring-plan.md +50 -0
- data/docs/architecture/persistence.md +45 -4
- data/docs/architecture/r8-unit1.md +104 -0
- data/docs/architecture/r8-unit10.md +72 -0
- data/docs/architecture/r8-unit11.md +71 -0
- data/docs/architecture/r8-unit12.md +67 -0
- data/docs/architecture/r8-unit13.md +61 -0
- data/docs/architecture/r8-unit14.md +72 -0
- data/docs/architecture/r8-unit15.md +65 -0
- data/docs/architecture/r8-unit16.md +54 -0
- data/docs/architecture/r8-unit17.md +73 -0
- data/docs/architecture/r8-unit18.md +92 -0
- data/docs/architecture/r8-unit19.md +61 -0
- data/docs/architecture/r8-unit2.md +55 -0
- data/docs/architecture/r8-unit20.md +51 -0
- data/docs/architecture/r8-unit21.md +79 -0
- data/docs/architecture/r8-unit22.md +74 -0
- data/docs/architecture/r8-unit23.md +81 -0
- data/docs/architecture/r8-unit3.md +130 -0
- data/docs/architecture/r8-unit4.md +79 -0
- data/docs/architecture/r8-unit5.md +115 -0
- data/docs/architecture/r8-unit6.md +71 -0
- data/docs/architecture/r8-unit7.md +70 -0
- data/docs/architecture/r8-unit8.md +78 -0
- data/docs/architecture/r8-unit9.md +73 -0
- data/docs/architecture/refactoring-closure.md +343 -0
- data/docs/architecture/remaining-refactoring-plan.md +390 -0
- data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +56 -0
- data/docs/architecture/tracing.md +2 -2
- data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
- data/docs/architecture.md +222 -0
- data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
- data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
- data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
- data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +7 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +7 -0
- data/docs/decisions/032-storage-backend-composition.md +85 -0
- data/docs/decisions/033-domain-persistence-ownership.md +79 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +78 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
- data/docs/decisions/036-context-contract-ownership.md +86 -0
- data/docs/decisions/037-common-definition-ownership.md +62 -0
- data/docs/decisions/038-responsibility-based-source-layout.md +145 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +82 -0
- data/docs/decisions/040-configuration-default-composition.md +105 -0
- data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
- data/docs/decisions/042-feature-owned-execution-state.md +106 -0
- data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
- data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
- data/docs/decisions/045-worker-input-restriction-ownership.md +104 -0
- data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
- data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
- data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
- data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
- data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +153 -0
- data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
- data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
- data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
- data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
- data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
- data/docs/decisions/058-neutral-storage-primitives.md +83 -0
- data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
- data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
- data/docs/decisions/061-persistence-failure-contracts.md +89 -0
- data/docs/decisions/062-content-store-service.md +52 -0
- data/docs/decisions/063-execution-contracts-and-services.md +88 -0
- data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
- data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
- data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
- data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
- data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
- data/docs/decisions/README.md +52 -6
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
- data/docs/execution-boundaries-preview.md +45 -0
- data/docs/features.md +16 -5
- data/docs/getting-started.md +11 -8
- data/docs/migrations/0.15.md +5 -0
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +8 -0
- data/docs/migrations/handoff-runner-multi-agent.md +37 -0
- data/docs/migrations/neutral-storage-spi.md +43 -0
- data/docs/migrations/parallel-tool-chat-removal.md +49 -0
- data/docs/migrations/persistence-failure-contracts.md +67 -0
- data/docs/migrations/r8-unit10.md +36 -0
- data/docs/migrations/r8-unit11.md +33 -0
- data/docs/migrations/r8-unit12.md +31 -0
- data/docs/migrations/r8-unit13.md +36 -0
- data/docs/migrations/r8-unit14.md +31 -0
- data/docs/migrations/r8-unit15.md +31 -0
- data/docs/migrations/r8-unit16.md +26 -0
- data/docs/migrations/r8-unit17.md +28 -0
- data/docs/migrations/r8-unit18.md +29 -0
- data/docs/migrations/r8-unit19.md +28 -0
- data/docs/migrations/r8-unit20.md +24 -0
- data/docs/migrations/r8-unit21.md +28 -0
- data/docs/migrations/r8-unit22.md +29 -0
- data/docs/migrations/r8-unit23.md +20 -0
- data/docs/migrations/r8-unit4.md +67 -0
- data/docs/migrations/r8-unit5.md +75 -0
- data/docs/migrations/r8-unit6.md +109 -0
- data/docs/migrations/r8-unit7.md +31 -0
- data/docs/migrations/r8-unit8.md +33 -0
- data/docs/migrations/r8-unit9.md +28 -0
- data/docs/migrations/shared-state-multi-agent.md +44 -0
- data/docs/migrations/storage-async-client.md +103 -0
- data/docs/migrations/storage-backend-composition.md +117 -0
- data/docs/migrations/storage-transaction-boundaries.md +74 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/persistence-backends.md +190 -215
- data/docs/runtime-and-concurrency.md +142 -32
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/{agent_already_exists_error.rb → agent/agent_already_exists_error.rb} +2 -0
- data/lib/phronomy/{agent_busy_error.rb → agent/agent_busy_error.rb} +2 -0
- data/lib/phronomy/{agent_purged_error.rb → agent/agent_purged_error.rb} +2 -0
- data/lib/phronomy/agent/api/agent.rb +17 -0
- data/lib/phronomy/{execution_rehydration_required_error.rb → agent/api/execution_rehydration_required_error.rb} +2 -0
- data/lib/phronomy/agent/async_event_api.rb +38 -33
- data/lib/phronomy/agent/base.rb +106 -294
- data/lib/phronomy/agent/before_llm_input.rb +64 -0
- data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
- data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
- data/lib/phronomy/agent/context_importer.rb +2 -2
- data/lib/phronomy/agent/{context_assembler.rb → context_preparation.rb} +157 -288
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
- data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +32 -63
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +195 -0
- data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +43 -23
- data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +8 -5
- data/lib/phronomy/agent/execution/execution_coordinator.rb +1744 -0
- data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
- data/lib/phronomy/agent/execution/execution_metadata.rb +54 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +288 -0
- data/lib/phronomy/agent/execution/execution_registry.rb +463 -0
- data/lib/phronomy/agent/execution/execution_session_runner.rb +113 -0
- data/lib/phronomy/agent/execution/initial_preparation.rb +323 -0
- data/lib/phronomy/agent/execution/invocation_actions.rb +368 -0
- data/lib/phronomy/agent/execution/invocation_transitions.rb +124 -0
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
- data/lib/phronomy/agent/execution_change.rb +161 -0
- data/lib/phronomy/agent/execution_environment.rb +36 -0
- data/lib/phronomy/agent/execution_extension_state.rb +21 -0
- data/lib/phronomy/agent/execution_observation.rb +35 -0
- data/lib/phronomy/agent/filterable.rb +182 -0
- data/lib/phronomy/agent/{handoff_context.rb → handoff/transfer_context.rb} +6 -6
- data/lib/phronomy/agent/{handoff_projection.rb → handoff/transfer_projection.rb} +14 -33
- data/lib/phronomy/agent/handoff_error.rb +7 -0
- data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
- data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
- data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
- data/lib/phronomy/agent/knowledge_item.rb +13 -0
- data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
- data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
- data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -15
- data/lib/phronomy/agent/persistence/agent_repository.rb +69 -0
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +338 -0
- data/lib/phronomy/agent/persistence/execution_repository.rb +124 -0
- data/lib/phronomy/agent/persistence/journal_repository.rb +63 -0
- data/lib/phronomy/agent/persistence/records.rb +33 -0
- data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +29 -0
- data/lib/phronomy/agent/persistence/watermark.rb +29 -0
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +124 -0
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +28 -51
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +50 -55
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +38 -43
- data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +26 -23
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +224 -0
- data/lib/phronomy/agent/reserved_execution.rb +16 -0
- data/lib/phronomy/agent/retention.rb +17 -0
- data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
- data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
- data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
- data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
- data/lib/phronomy/agent/{ruby_llm_materializer.rb → runtime_input.rb} +19 -39
- data/lib/phronomy/agent/saved_context_reader.rb +55 -0
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/state_writer.rb +166 -0
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/{stream_callback_error.rb → agent/stream_callback_error.rb} +2 -0
- data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
- data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +30 -0
- data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +114 -248
- data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
- data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
- data/lib/phronomy/common/configuration_error.rb +7 -0
- data/lib/phronomy/common/error.rb +5 -0
- data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
- data/lib/phronomy/common/values/serializable.rb +32 -0
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/storage_schema.rb +12 -0
- data/lib/phronomy/content_store/stored_contents.rb +44 -0
- data/lib/phronomy/context/assembly.rb +202 -0
- data/lib/phronomy/{context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +5 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent → context}/context_plan.rb +2 -2
- data/lib/phronomy/{agent → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent → context}/context_policy_input.rb +7 -7
- data/lib/phronomy/{agent → context}/context_policy_input_builder.rb +1 -1
- data/lib/phronomy/context/default_policy.rb +140 -0
- data/lib/phronomy/context/final_budget_validator.rb +33 -0
- data/lib/phronomy/{agent → context}/llm_input_build_context.rb +2 -2
- data/lib/phronomy/{agent → context}/llm_input_manifest.rb +21 -21
- data/lib/phronomy/{agent → context}/llm_input_patch.rb +3 -3
- data/lib/phronomy/context/manifest_error.rb +10 -0
- data/lib/phronomy/context/prompt_template.rb +98 -0
- data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
- data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
- data/lib/phronomy/embeddings/async/async_client.rb +44 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/transport_error.rb +11 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +39 -20
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +1 -1
- data/lib/phronomy/engine/event_loop.rb +164 -630
- data/lib/phronomy/engine/execution_binding.rb +23 -0
- data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
- data/lib/phronomy/engine/fsm_protocol.rb +9 -0
- data/lib/phronomy/engine/fsm_session.rb +37 -30
- data/lib/phronomy/engine/metrics.rb +28 -0
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +104 -95
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/execution/backpressure_error.rb +7 -0
- data/lib/phronomy/{blocking.rb → execution/blocking.rb} +2 -2
- data/lib/phronomy/execution/cancellation_error.rb +7 -0
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +5 -1
- data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
- data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
- data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
- data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
- data/lib/phronomy/execution/concurrency/worker_input_restricted.rb +14 -0
- data/lib/phronomy/execution/event_loop_reentrancy_error.rb +8 -0
- data/lib/phronomy/{execution.rb → execution/execution.rb} +57 -30
- data/lib/phronomy/{execution_cancellation_error.rb → execution/execution_cancellation_error.rb} +2 -0
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{execution_timeout_error.rb → execution/execution_timeout_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_entry_action_error.rb → execution/invalid_async_entry_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_transition_action_error.rb → execution/invalid_async_transition_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_workflow_action_error.rb → execution/invalid_async_workflow_action_error.rb} +2 -0
- data/lib/phronomy/{invocation_context.rb → execution/invocation_context.rb} +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/pool_shutdown_error.rb +7 -0
- data/lib/phronomy/execution/recursion_limit_error.rb +7 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/execution/runtime_shutdown_error.rb +7 -0
- data/lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb +7 -0
- data/lib/phronomy/execution/scheduler_reentrancy_error.rb +9 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +7 -3
- data/lib/phronomy/execution/timeout_error.rb +7 -0
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/filter_block_error.rb +14 -0
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
- data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
- data/lib/phronomy/generation/generator_verifier.rb +118 -0
- data/lib/phronomy/generation/low_confidence_error.rb +14 -0
- data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
- data/lib/phronomy/llm_adapter/authentication_error.rb +9 -0
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/llm_adapter/context_length_error.rb +9 -0
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/llm_adapter/rate_limit_error.rb +9 -0
- data/lib/phronomy/llm_adapter/request.rb +79 -0
- data/lib/phronomy/llm_adapter/response.rb +36 -0
- data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
- data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
- data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
- data/lib/phronomy/llm_adapter.rb +3 -16
- data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
- data/lib/phronomy/multi_agent/admission_registry.rb +15 -2
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
- data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
- data/lib/phronomy/{agent → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +156 -0
- data/lib/phronomy/{agent → multi_agent}/handoff_state.rb +2 -2
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +75 -0
- data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
- data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
- data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +124 -0
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +69 -0
- data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
- data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
- data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +87 -75
- data/lib/phronomy/multi_agent/team_execution.rb +1 -1
- data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +5 -5
- data/lib/phronomy/multi_agent/team_root.rb +1 -1
- data/lib/phronomy/output_parser/parse_error.rb +7 -0
- data/lib/phronomy/persistence/content_repository.rb +26 -0
- data/lib/phronomy/persistence/errors.rb +16 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +21 -21
- data/lib/phronomy/persistence/persistence.rb +23 -0
- data/lib/phronomy/persistence/save_outcome.rb +24 -0
- data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
- data/lib/phronomy/persistence/storage_boundary.rb +32 -0
- data/lib/phronomy/persistence/transaction.rb +86 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +29 -0
- data/lib/phronomy/persistence_composition/stores.rb +34 -0
- data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
- data/lib/phronomy/runtime_composition/agent_defaults.rb +11 -0
- data/lib/phronomy/runtime_composition/configuration.rb +146 -0
- data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
- data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
- data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
- data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
- data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
- data/lib/phronomy/storage/async/async_client.rb +60 -0
- data/lib/phronomy/storage/backend.rb +107 -0
- data/lib/phronomy/storage/backends/in_memory.rb +157 -0
- data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
- data/lib/phronomy/storage/blobs.rb +31 -0
- data/lib/phronomy/storage/condition.rb +25 -0
- data/lib/phronomy/storage/condition_failed_error.rb +16 -0
- data/lib/phronomy/storage/conflict_error.rb +9 -0
- data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
- data/lib/phronomy/storage/entry.rb +33 -0
- data/lib/phronomy/storage/guard_ref.rb +13 -0
- data/lib/phronomy/storage/not_found_error.rb +9 -0
- data/lib/phronomy/storage/record_codec.rb +177 -0
- data/lib/phronomy/storage/records.rb +61 -0
- data/lib/phronomy/storage/resource.rb +126 -0
- data/lib/phronomy/storage/scope.rb +25 -0
- data/lib/phronomy/storage/serialization_error.rb +9 -0
- data/lib/phronomy/storage/streams.rb +44 -0
- data/lib/phronomy/storage/transaction_error.rb +10 -0
- data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
- data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
- data/lib/phronomy/storage/validation.rb +53 -0
- data/lib/phronomy/storage/view.rb +131 -0
- data/lib/phronomy/testing/eval/metrics.rb +1 -1
- data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +54 -48
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +52 -46
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +124 -0
- data/lib/phronomy/testing/persistence_contract.rb +4 -0
- data/lib/phronomy/tool/authorization.rb +77 -0
- data/lib/phronomy/tool/base.rb +549 -9
- data/lib/phronomy/tool/call_request.rb +40 -0
- data/lib/phronomy/{agent/tool_definition_set.rb → tool/definition_set.rb} +16 -12
- data/lib/phronomy/tool/operation.rb +88 -0
- data/lib/phronomy/tool/schema.rb +214 -0
- data/lib/phronomy/tool/settings.rb +31 -0
- data/lib/phronomy/tool/tool_error.rb +7 -0
- data/lib/phronomy/{agent → tool}/tool_executor.rb +10 -7
- data/lib/phronomy/tools/agent.rb +22 -90
- data/lib/phronomy/tools/mcp.rb +21 -14
- data/lib/phronomy/tools/vector_search.rb +4 -4
- data/lib/phronomy/tracing/automatic.rb +11 -6
- data/lib/phronomy/tracing/base.rb +1 -1
- data/lib/phronomy/tracing/observation.rb +37 -0
- data/lib/phronomy/tracing/settings.rb +36 -0
- data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
- data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
- data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
- data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
- data/lib/phronomy/vector_store/base.rb +118 -49
- data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
- data/lib/phronomy/vector_store.rb +2 -17
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/completion.rb +11 -0
- data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +5 -1
- data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +186 -0
- data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +145 -115
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/persistence/codec.rb +153 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +63 -0
- data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_terminal_policy.rb +40 -0
- data/lib/phronomy.rb +97 -98
- data/scripts/api_snapshot.rb +36 -16
- data/scripts/storage_spi_snapshot.rb +36 -0
- data/sig/_private/phronomy/agent/settings.rbs +19 -0
- data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
- data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
- data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
- data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
- data/sig/_private/phronomy/tool/settings.rbs +13 -0
- data/sig/_private/phronomy/tracing/settings.rbs +15 -0
- data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
- data/sig/phronomy/agent/admission.rbs +19 -0
- data/sig/phronomy/agent/execution_contract.rbs +132 -0
- data/sig/phronomy/agent/execution_environment.rbs +20 -0
- data/sig/phronomy/agent/store.rbs +73 -0
- data/sig/phronomy/agent.rbs +14 -139
- data/sig/phronomy/common.rbs +7 -0
- data/sig/phronomy/content_store/contracts.rbs +13 -0
- data/sig/phronomy/context.rbs +152 -0
- data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
- data/sig/phronomy/embeddings/base.rbs +17 -0
- data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
- data/sig/phronomy/engine/control_bindings.rbs +11 -0
- data/sig/phronomy/engine/control_protocols.rbs +23 -0
- data/sig/phronomy/engine/execution_binding.rbs +6 -0
- data/sig/phronomy/engine/execution_receiver.rbs +15 -0
- data/sig/phronomy/engine/observability.rbs +13 -0
- data/sig/phronomy/execution/async_operation.rbs +7 -0
- data/sig/phronomy/execution/blocking.rbs +5 -0
- data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
- data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
- data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
- data/sig/phronomy/execution/control_protocols.rbs +18 -0
- data/sig/phronomy/execution/errors.rbs +7 -0
- data/sig/phronomy/execution/execution.rbs +41 -0
- data/sig/phronomy/execution/execution_receiver.rbs +40 -0
- data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
- data/sig/phronomy/execution/invocation_context.rbs +18 -0
- data/sig/phronomy/execution/invocation_controls.rbs +13 -0
- data/sig/phronomy/execution/task_result.rbs +29 -0
- data/sig/phronomy/extensions.rbs +0 -21
- data/sig/phronomy/handoff.rbs +12 -4
- data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
- data/sig/phronomy/llm_adapter/base.rbs +14 -0
- data/sig/phronomy/llm_adapter/usage.rbs +14 -0
- data/sig/phronomy/llm_adapter/values.rbs +43 -0
- data/sig/phronomy/llm_context_window.rbs +10 -0
- data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
- data/sig/phronomy/multi_agent/store.rbs +54 -0
- data/sig/phronomy/multi_agent.rbs +24 -3
- data/sig/phronomy/persistence/errors.rbs +18 -0
- data/sig/phronomy/persistence/migration.rbs +13 -0
- data/sig/phronomy/persistence/transaction.rbs +19 -0
- data/sig/phronomy/persistence.rbs +3 -185
- data/sig/phronomy/persistence_composition.rbs +19 -0
- data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
- data/sig/phronomy/storage/async/async_client.rbs +8 -0
- data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
- data/sig/phronomy/storage/contracts.rbs +160 -0
- data/sig/phronomy/tool.rbs +58 -5
- data/sig/phronomy/tracing/observation.rbs +7 -0
- data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
- data/sig/phronomy/vector_store/base.rbs +23 -0
- data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
- data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
- data/sig/phronomy/workflow/store.rbs +8 -0
- data/sig/phronomy/workflow.rbs +1 -1
- data/sig/phronomy.rbs +0 -49
- metadata +462 -130
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +0 -584
- data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
- data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
- data/lib/phronomy/agent/concerns/filterable.rb +0 -184
- data/lib/phronomy/agent/context/capability/base.rb +0 -507
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
- data/lib/phronomy/agent/context_policies/default.rb +0 -142
- data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
- data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
- data/lib/phronomy/agent/handoff_request.rb +0 -45
- data/lib/phronomy/agent/handoff_runner.rb +0 -178
- data/lib/phronomy/agent/phase_machine_builder.rb +0 -152
- data/lib/phronomy/agent/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/recovery_support.rb +0 -504
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
- data/lib/phronomy/agent/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration.rb +0 -59
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/generator_verifier.rb +0 -369
- data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/metrics.rb +0 -26
- data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
- data/lib/phronomy/persistence/durable_codec.rb +0 -706
- data/lib/phronomy/persistence/in_memory.rb +0 -690
- data/lib/phronomy/persistence/repository_facades.rb +0 -535
- data/lib/phronomy/persistence.rb +0 -276
- data/lib/phronomy/recovery.rb +0 -186
- data/lib/phronomy/ruby_llm_patches.rb +0 -24
- data/lib/phronomy/runnable.rb +0 -54
- data/lib/phronomy/token_usage.rb +0 -85
- data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
- data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
- data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
- data/lib/phronomy/workflow_recovery.rb +0 -123
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
- /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
- /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Agent
|
|
5
|
-
module Concerns
|
|
6
|
-
# Adds input, output, and tool-result filter support to an agent.
|
|
7
|
-
#
|
|
8
|
-
# Filters transform (or block) values at three call sites:
|
|
9
|
-
# - *input* — the raw user input string, before the LLM is called
|
|
10
|
-
# - *output* — the final LLM output string, before it is returned
|
|
11
|
-
# - *tool result* — the return value of each tool call
|
|
12
|
-
#
|
|
13
|
-
# Each filter in the chain receives the value returned by the previous one.
|
|
14
|
-
# A {Phronomy::FilterBlockError} raised inside any filter propagates to the
|
|
15
|
-
# caller.
|
|
16
|
-
#
|
|
17
|
-
# @api private
|
|
18
|
-
module Filterable
|
|
19
|
-
def self.included(base)
|
|
20
|
-
base.extend(ClassMethods)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Class-level DSL mixed into the including agent class.
|
|
24
|
-
module ClassMethods
|
|
25
|
-
# Registers a filter applied to every invocation's user input.
|
|
26
|
-
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
27
|
-
# when a class is given it is instantiated with +.new+.
|
|
28
|
-
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
29
|
-
# @return [void]
|
|
30
|
-
# @api public
|
|
31
|
-
def input_filter(filter)
|
|
32
|
-
@_class_input_filters ||= []
|
|
33
|
-
@_class_input_filters << _resolve_filter(filter)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Registers a filter applied to every invocation's final LLM output.
|
|
37
|
-
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
38
|
-
# when a class is given it is instantiated with +.new+.
|
|
39
|
-
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
40
|
-
# @return [void]
|
|
41
|
-
# @api public
|
|
42
|
-
def output_filter(filter)
|
|
43
|
-
@_class_output_filters ||= []
|
|
44
|
-
@_class_output_filters << _resolve_filter(filter)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Registers a filter applied to every tool result for all tools.
|
|
48
|
-
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
49
|
-
# when a class is given it is instantiated with +.new+.
|
|
50
|
-
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
51
|
-
# @return [void]
|
|
52
|
-
# @api public
|
|
53
|
-
def tool_result_filter(filter)
|
|
54
|
-
@_class_tool_result_filters ||= []
|
|
55
|
-
@_class_tool_result_filters << _resolve_filter(filter)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# @return [Array<Phronomy::Filter::Base>]
|
|
59
|
-
# @api private
|
|
60
|
-
def _class_input_filters
|
|
61
|
-
@_class_input_filters || []
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# @return [Array<Phronomy::Filter::Base>]
|
|
65
|
-
# @api private
|
|
66
|
-
def _class_output_filters
|
|
67
|
-
@_class_output_filters || []
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# @return [Array<Phronomy::Filter::Base>]
|
|
71
|
-
# @api private
|
|
72
|
-
def _class_tool_result_filters
|
|
73
|
-
@_class_tool_result_filters || []
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
private
|
|
77
|
-
|
|
78
|
-
# Coerce +filter+ to an instance: if a Class is passed, call +.new+;
|
|
79
|
-
# otherwise return the object as-is.
|
|
80
|
-
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
81
|
-
# @return [Phronomy::Filter::Base]
|
|
82
|
-
# @api private
|
|
83
|
-
def _resolve_filter(filter)
|
|
84
|
-
filter.is_a?(Class) ? filter.new : filter
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# Registers an input filter on this instance.
|
|
89
|
-
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
90
|
-
# when a class is given it is instantiated with +.new+.
|
|
91
|
-
# Runs in addition to any class-level input filters.
|
|
92
|
-
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
93
|
-
# @return [self]
|
|
94
|
-
# @api public
|
|
95
|
-
def add_input_filter(filter)
|
|
96
|
-
@_instance_input_filters ||= []
|
|
97
|
-
@_instance_input_filters << _resolve_filter(filter)
|
|
98
|
-
self
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# Registers an output filter on this instance.
|
|
102
|
-
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
103
|
-
# when a class is given it is instantiated with +.new+.
|
|
104
|
-
# Runs in addition to any class-level output filters.
|
|
105
|
-
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
106
|
-
# @return [self]
|
|
107
|
-
# @api public
|
|
108
|
-
def add_output_filter(filter)
|
|
109
|
-
@_instance_output_filters ||= []
|
|
110
|
-
@_instance_output_filters << _resolve_filter(filter)
|
|
111
|
-
self
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# Registers a tool result filter on this instance.
|
|
115
|
-
#
|
|
116
|
-
# When called with two arguments, the filter is scoped to the given tool
|
|
117
|
-
# class only. When called with one argument, it applies to all tools.
|
|
118
|
-
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
119
|
-
# when a class is given it is instantiated with +.new+.
|
|
120
|
-
#
|
|
121
|
-
# @overload add_tool_result_filter(filter)
|
|
122
|
-
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>] applied to every tool
|
|
123
|
-
# @overload add_tool_result_filter(tool_class, filter)
|
|
124
|
-
# @param tool_class [Class] scope the filter to this tool
|
|
125
|
-
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
126
|
-
# @return [self]
|
|
127
|
-
# @api public
|
|
128
|
-
def add_tool_result_filter(tool_class_or_filter, filter = nil)
|
|
129
|
-
if filter.nil?
|
|
130
|
-
# Single-argument form: apply to all tools.
|
|
131
|
-
@_instance_tool_result_filters ||= []
|
|
132
|
-
@_instance_tool_result_filters << _resolve_filter(tool_class_or_filter)
|
|
133
|
-
else
|
|
134
|
-
# Two-argument form: scoped to one tool class.
|
|
135
|
-
@_scoped_tool_result_filters ||= {}
|
|
136
|
-
(@_scoped_tool_result_filters[tool_class_or_filter] ||= []) << _resolve_filter(filter)
|
|
137
|
-
end
|
|
138
|
-
self
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
private
|
|
142
|
-
|
|
143
|
-
# Run input filters (class-level then instance-level).
|
|
144
|
-
# @param input [String, Hash] the raw user input
|
|
145
|
-
# @return [String, Hash] the (possibly transformed) input
|
|
146
|
-
# @api private
|
|
147
|
-
def run_input_filters!(input)
|
|
148
|
-
class_filters = self.class._class_input_filters
|
|
149
|
-
inst_filters = @_instance_input_filters || []
|
|
150
|
-
(class_filters + inst_filters).inject(input) { |val, f| f.call(val) }
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# Run output filters (class-level then instance-level).
|
|
154
|
-
# @param output [String] the LLM output
|
|
155
|
-
# @return [String] the (possibly transformed) output
|
|
156
|
-
# @api private
|
|
157
|
-
def run_output_filters!(output)
|
|
158
|
-
class_filters = self.class._class_output_filters
|
|
159
|
-
inst_filters = @_instance_output_filters || []
|
|
160
|
-
(class_filters + inst_filters).inject(output) { |val, f| f.call(val) }
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
# Collect all tool-result filters (global + scoped) for a given tool class.
|
|
164
|
-
# @param tool_class [Class]
|
|
165
|
-
# @return [Array<Phronomy::Filter::Base>]
|
|
166
|
-
# @api private
|
|
167
|
-
def _tool_result_filters_for(tool_class)
|
|
168
|
-
global = self.class._class_tool_result_filters + (@_instance_tool_result_filters || [])
|
|
169
|
-
scoped = (@_scoped_tool_result_filters || {})[tool_class] || []
|
|
170
|
-
global + scoped
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
# Coerce +filter+ to an instance: if a Class is passed, call +.new+;
|
|
174
|
-
# otherwise return the object as-is.
|
|
175
|
-
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
176
|
-
# @return [Phronomy::Filter::Base]
|
|
177
|
-
# @api private
|
|
178
|
-
def _resolve_filter(filter)
|
|
179
|
-
filter.is_a?(Class) ? filter.new : filter
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
end
|
|
@@ -1,507 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Agent
|
|
5
|
-
module Context
|
|
6
|
-
module Capability
|
|
7
|
-
# Base class extending RubyLLM::Tool with Phronomy-specific DSL.
|
|
8
|
-
class Base < RubyLLM::Tool
|
|
9
|
-
class << self
|
|
10
|
-
# @api public
|
|
11
|
-
def tool_name(value = nil)
|
|
12
|
-
return @tool_name if value.nil?
|
|
13
|
-
|
|
14
|
-
@tool_name = value.to_s
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# RubyLLM stores Tool descriptions in a class-instance variable.
|
|
18
|
-
# Preserve normal class inheritance semantics so Phronomy's anonymous
|
|
19
|
-
# decorator subclasses do not lose their parent's description.
|
|
20
|
-
# @api public
|
|
21
|
-
def description(text = nil)
|
|
22
|
-
unless text
|
|
23
|
-
return @description if instance_variable_defined?(:@description)
|
|
24
|
-
return superclass.description if superclass.respond_to?(:description)
|
|
25
|
-
|
|
26
|
-
return nil
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
@description = text
|
|
30
|
-
end
|
|
31
|
-
alias_method :desc, :description
|
|
32
|
-
|
|
33
|
-
# RubyLLM stores declared parameters in a class-instance variable.
|
|
34
|
-
# Copy the parent's registry on first access so child classes inherit
|
|
35
|
-
# existing parameters while remaining free to add their own.
|
|
36
|
-
# @api public
|
|
37
|
-
def parameters
|
|
38
|
-
return @parameters if instance_variable_defined?(:@parameters)
|
|
39
|
-
|
|
40
|
-
parent = superclass.respond_to?(:parameters) ? superclass.parameters : {}
|
|
41
|
-
@parameters = parent.dup
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# RubyLLM stores an explicit .params schema definition in a
|
|
45
|
-
# class-instance variable. Readers must fall back to the parent.
|
|
46
|
-
# @api public
|
|
47
|
-
def params_schema_definition
|
|
48
|
-
return @params_schema_definition if instance_variable_defined?(:@params_schema_definition)
|
|
49
|
-
return superclass.params_schema_definition if superclass.respond_to?(:params_schema_definition)
|
|
50
|
-
|
|
51
|
-
nil
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# RubyLLM provider params are also class-instance state. Copy them on
|
|
55
|
-
# first access to preserve inheritance without sharing the top-level
|
|
56
|
-
# mutable Hash between parent and child.
|
|
57
|
-
# @api public
|
|
58
|
-
def provider_params
|
|
59
|
-
return @provider_params if instance_variable_defined?(:@provider_params)
|
|
60
|
-
|
|
61
|
-
parent = superclass.respond_to?(:provider_params) ? superclass.provider_params : {}
|
|
62
|
-
@provider_params = duplicate_configuration(parent)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# @api public
|
|
66
|
-
def param(name, enum: nil, properties: nil, **options)
|
|
67
|
-
super(name, **options)
|
|
68
|
-
param_enums[name] = duplicate_configuration(enum) if enum
|
|
69
|
-
param_schemas[name] = normalize_nested_schema(properties) if properties
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# @api public
|
|
73
|
-
def param_enums
|
|
74
|
-
return @param_enums if instance_variable_defined?(:@param_enums)
|
|
75
|
-
|
|
76
|
-
parent = superclass.respond_to?(:param_enums) ? superclass.param_enums : {}
|
|
77
|
-
@param_enums = duplicate_configuration(parent)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# @api public
|
|
81
|
-
def param_schemas
|
|
82
|
-
return @param_schemas if instance_variable_defined?(:@param_schemas)
|
|
83
|
-
|
|
84
|
-
parent = superclass.respond_to?(:param_schemas) ? superclass.param_schemas : {}
|
|
85
|
-
@param_schemas = duplicate_configuration(parent)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
private
|
|
89
|
-
|
|
90
|
-
def duplicate_configuration(value)
|
|
91
|
-
case value
|
|
92
|
-
when Hash
|
|
93
|
-
value.to_h do |key, child|
|
|
94
|
-
[key, duplicate_configuration(child)]
|
|
95
|
-
end
|
|
96
|
-
when Array
|
|
97
|
-
value.map { |child| duplicate_configuration(child) }
|
|
98
|
-
else
|
|
99
|
-
value
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def normalize_nested_schema(props)
|
|
104
|
-
props.transform_keys(&:to_sym).transform_values do |spec|
|
|
105
|
-
normalized = spec.transform_keys(&:to_sym)
|
|
106
|
-
normalized[:type] ||= :string
|
|
107
|
-
if normalized[:properties]
|
|
108
|
-
normalized[:properties] = normalize_nested_schema(normalized[:properties])
|
|
109
|
-
end
|
|
110
|
-
normalized
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
public
|
|
115
|
-
|
|
116
|
-
# Declares whether Tool work is safe to run cooperatively on the EventLoop
|
|
117
|
-
# or must be offloaded to a bounded worker pool.
|
|
118
|
-
#
|
|
119
|
-
# Phronomy does not classify the reason for offloading. Blocking I/O,
|
|
120
|
-
# CPU-bound synchronous work, and other long synchronous calls all use
|
|
121
|
-
# +:offloaded+. The application owns that workload classification.
|
|
122
|
-
# @api public
|
|
123
|
-
def execution_mode(value = nil)
|
|
124
|
-
if value.nil?
|
|
125
|
-
return @execution_mode if instance_variable_defined?(:@execution_mode)
|
|
126
|
-
return superclass.execution_mode if superclass.respond_to?(:execution_mode)
|
|
127
|
-
|
|
128
|
-
return :offloaded
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
valid = %i[cooperative offloaded]
|
|
132
|
-
unless valid.include?(value)
|
|
133
|
-
raise ArgumentError,
|
|
134
|
-
"execution_mode must be one of #{valid.inspect}, got #{value.inspect}"
|
|
135
|
-
end
|
|
136
|
-
@execution_mode = value
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# Configures execution-error handling. Supported values are :raise
|
|
140
|
-
# and :suppress only.
|
|
141
|
-
# @api public
|
|
142
|
-
def on_error(behavior = nil)
|
|
143
|
-
if behavior.nil?
|
|
144
|
-
return @on_error if instance_variable_defined?(:@on_error)
|
|
145
|
-
return superclass.on_error if superclass.respond_to?(:on_error)
|
|
146
|
-
|
|
147
|
-
return :raise
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
valid = %i[raise suppress]
|
|
151
|
-
unless valid.include?(behavior)
|
|
152
|
-
raise ArgumentError,
|
|
153
|
-
"on_error must be one of #{valid.inspect}, got #{behavior.inspect}"
|
|
154
|
-
end
|
|
155
|
-
@on_error = behavior
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
# @api public
|
|
159
|
-
def on_schema_error(behavior = nil)
|
|
160
|
-
if behavior.nil?
|
|
161
|
-
return @on_schema_error if instance_variable_defined?(:@on_schema_error)
|
|
162
|
-
return superclass.on_schema_error if superclass.respond_to?(:on_schema_error)
|
|
163
|
-
|
|
164
|
-
return :return_error
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
@on_schema_error = behavior
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
# @api public
|
|
171
|
-
def requires_approval(value = :__unset__, &block)
|
|
172
|
-
if block
|
|
173
|
-
unless value == :__unset__
|
|
174
|
-
raise ArgumentError, "pass either a value or a block to requires_approval"
|
|
175
|
-
end
|
|
176
|
-
@requires_approval = block
|
|
177
|
-
elsif value == :__unset__
|
|
178
|
-
return @requires_approval if instance_variable_defined?(:@requires_approval)
|
|
179
|
-
return superclass.requires_approval if superclass.respond_to?(:requires_approval)
|
|
180
|
-
|
|
181
|
-
false
|
|
182
|
-
else
|
|
183
|
-
unless value == true || value == false || value.respond_to?(:call)
|
|
184
|
-
raise ArgumentError, "requires_approval must be true, false, or callable"
|
|
185
|
-
end
|
|
186
|
-
@requires_approval = value
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
# @api public
|
|
191
|
-
def approval_facts(&block)
|
|
192
|
-
if block
|
|
193
|
-
@approval_facts = block
|
|
194
|
-
elsif instance_variable_defined?(:@approval_facts)
|
|
195
|
-
@approval_facts
|
|
196
|
-
elsif superclass.respond_to?(:approval_facts)
|
|
197
|
-
superclass.approval_facts
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
# @api public
|
|
202
|
-
def redact_params(*names)
|
|
203
|
-
if names.empty?
|
|
204
|
-
parent = superclass.respond_to?(:redact_params) ? superclass.redact_params : []
|
|
205
|
-
((@redacted_params || []) + parent).uniq
|
|
206
|
-
else
|
|
207
|
-
@redacted_params = ((@redacted_params || []) + names.map(&:to_sym)).uniq
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
# @api public
|
|
212
|
-
def max_result_size(value = :__unset__)
|
|
213
|
-
if value == :__unset__
|
|
214
|
-
return @max_result_size if instance_variable_defined?(:@max_result_size)
|
|
215
|
-
return superclass.max_result_size if superclass.respond_to?(:max_result_size)
|
|
216
|
-
|
|
217
|
-
return nil
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
@max_result_size = value
|
|
221
|
-
end
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
def name
|
|
225
|
-
self.class.tool_name || super
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
def params_schema
|
|
229
|
-
schema = super
|
|
230
|
-
return schema if schema.nil?
|
|
231
|
-
|
|
232
|
-
properties = schema.dig("properties") || schema.dig(:properties)
|
|
233
|
-
return schema unless properties
|
|
234
|
-
|
|
235
|
-
self.class.param_enums.each do |param_name, values|
|
|
236
|
-
key = properties.key?(param_name.to_s) ? param_name.to_s : param_name.to_sym
|
|
237
|
-
next unless properties[key]
|
|
238
|
-
|
|
239
|
-
param_type = properties[key]["type"]
|
|
240
|
-
properties[key]["enum"] = values.map do |value|
|
|
241
|
-
case param_type
|
|
242
|
-
when "integer"
|
|
243
|
-
value.is_a?(Integer) ? value : Integer(value.to_s)
|
|
244
|
-
when "number"
|
|
245
|
-
value.is_a?(Numeric) ? value : Float(value.to_s)
|
|
246
|
-
when "boolean"
|
|
247
|
-
unless value == true || value == false
|
|
248
|
-
raise ArgumentError,
|
|
249
|
-
"boolean enum values must be true or false (got: #{value.inspect})"
|
|
250
|
-
end
|
|
251
|
-
value
|
|
252
|
-
else
|
|
253
|
-
value.to_s
|
|
254
|
-
end
|
|
255
|
-
end
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
self.class.param_schemas.each do |param_name, nested|
|
|
259
|
-
key = properties.key?(param_name.to_s) ? param_name.to_s : param_name.to_sym
|
|
260
|
-
next unless properties[key]
|
|
261
|
-
properties[key]["properties"] = nested_schema_to_json_schema(nested)
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
schema
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
# @api public
|
|
268
|
-
def call(args, cancellation_token: nil)
|
|
269
|
-
cancellation_token&.raise_if_cancelled!
|
|
270
|
-
validated_args, schema_error = validate_and_coerce(args)
|
|
271
|
-
if schema_error
|
|
272
|
-
case self.class.on_schema_error
|
|
273
|
-
when :raise
|
|
274
|
-
raise Phronomy::ToolError,
|
|
275
|
-
"#{self.class.name} schema error: #{schema_error}"
|
|
276
|
-
else
|
|
277
|
-
return "Schema validation failed: #{schema_error}"
|
|
278
|
-
end
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
if cancellation_token && execute_accepts_cancellation_token?
|
|
282
|
-
validated_args = validated_args.merge(cancellation_token: cancellation_token)
|
|
283
|
-
end
|
|
284
|
-
result = super(validated_args)
|
|
285
|
-
truncate_result_if_needed(result)
|
|
286
|
-
rescue Phronomy::ToolError, Phronomy::CancellationError
|
|
287
|
-
raise
|
|
288
|
-
rescue => error
|
|
289
|
-
if self.class.on_error == :suppress
|
|
290
|
-
msg = "[Phronomy] Tool #{self.class.name} suppressed error: " \
|
|
291
|
-
"#{error.class}: #{error.message}"
|
|
292
|
-
if Phronomy.configuration.logger
|
|
293
|
-
Phronomy.configuration.logger.warn(msg)
|
|
294
|
-
else
|
|
295
|
-
warn msg
|
|
296
|
-
end
|
|
297
|
-
"Tool error suppressed: #{error.message}"
|
|
298
|
-
else
|
|
299
|
-
raise Phronomy::ToolError,
|
|
300
|
-
"#{self.class.name} execution failed: #{error.message}"
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
# @api public
|
|
305
|
-
def call_async(
|
|
306
|
-
args,
|
|
307
|
-
cancellation_token: nil,
|
|
308
|
-
config: {}
|
|
309
|
-
)
|
|
310
|
-
Phronomy::Agent::ToolExecutor.call_async(
|
|
311
|
-
tool: self,
|
|
312
|
-
args: args,
|
|
313
|
-
cancellation_token: cancellation_token,
|
|
314
|
-
config: config
|
|
315
|
-
)
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
def requires_approval
|
|
319
|
-
self.class.requires_approval
|
|
320
|
-
end
|
|
321
|
-
|
|
322
|
-
def requires_approval?
|
|
323
|
-
self.class.requires_approval
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
# @api public
|
|
327
|
-
def tool_origin
|
|
328
|
-
:local
|
|
329
|
-
end
|
|
330
|
-
|
|
331
|
-
# @api public
|
|
332
|
-
def approval_metadata
|
|
333
|
-
{}
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
# @api public
|
|
337
|
-
def execute(**_args)
|
|
338
|
-
raise NotImplementedError, "#{self.class}#execute is not implemented"
|
|
339
|
-
end
|
|
340
|
-
|
|
341
|
-
private
|
|
342
|
-
|
|
343
|
-
def execute_accepts_cancellation_token?
|
|
344
|
-
method(:execute).parameters.any? do |type, name|
|
|
345
|
-
name == :cancellation_token && %i[key keyreq].include?(type)
|
|
346
|
-
end
|
|
347
|
-
end
|
|
348
|
-
|
|
349
|
-
def truncate_result_if_needed(result)
|
|
350
|
-
max = self.class.max_result_size || Phronomy.configuration.tool_result_max_size
|
|
351
|
-
return result unless max && result.respond_to?(:length) && result.length > max
|
|
352
|
-
|
|
353
|
-
msg = "[Phronomy] Tool #{self.class.name} result truncated " \
|
|
354
|
-
"(#{result.length} chars > #{max} limit)"
|
|
355
|
-
if Phronomy.configuration.logger
|
|
356
|
-
Phronomy.configuration.logger.warn(msg)
|
|
357
|
-
else
|
|
358
|
-
warn msg
|
|
359
|
-
end
|
|
360
|
-
"#{result[0, max]}...[truncated]"
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
def redacted_args(args)
|
|
364
|
-
redacted = self.class.redact_params
|
|
365
|
-
return args if redacted.empty?
|
|
366
|
-
|
|
367
|
-
args.each_with_object({}) do |(key, value), result|
|
|
368
|
-
result[key] = redacted.include?(key.to_sym) ? "[REDACTED]" : value
|
|
369
|
-
end
|
|
370
|
-
end
|
|
371
|
-
|
|
372
|
-
def validate_and_coerce(args)
|
|
373
|
-
return [args, nil] if self.class.parameters.empty?
|
|
374
|
-
|
|
375
|
-
normalized = (args || {}).transform_keys(&:to_sym)
|
|
376
|
-
coerce_mode = self.class.on_schema_error == :coerce
|
|
377
|
-
result = {}
|
|
378
|
-
|
|
379
|
-
self.class.parameters.each do |name, param|
|
|
380
|
-
value = normalized[name]
|
|
381
|
-
if value.nil?
|
|
382
|
-
return [nil, "required parameter '#{name}' is missing"] if param.required
|
|
383
|
-
next
|
|
384
|
-
end
|
|
385
|
-
|
|
386
|
-
if coerce_mode
|
|
387
|
-
coerced, error = coerce_value(value, param.type)
|
|
388
|
-
return [nil, error] if error
|
|
389
|
-
value = coerced
|
|
390
|
-
else
|
|
391
|
-
error = type_error(value, param.type)
|
|
392
|
-
return [nil, error] if error
|
|
393
|
-
end
|
|
394
|
-
|
|
395
|
-
if param.type.to_sym == :object
|
|
396
|
-
nested_schema = self.class.param_schemas[name]
|
|
397
|
-
if nested_schema
|
|
398
|
-
error = validate_nested_object(value, nested_schema, name.to_s)
|
|
399
|
-
return [nil, error] if error
|
|
400
|
-
end
|
|
401
|
-
end
|
|
402
|
-
|
|
403
|
-
enum_vals = self.class.param_enums[name]
|
|
404
|
-
if enum_vals && !enum_vals.map(&:to_s).include?(value.to_s)
|
|
405
|
-
return [nil,
|
|
406
|
-
"parameter '#{name}' must be one of: #{enum_vals.join(", ")} " \
|
|
407
|
-
"(got: #{value.inspect})"]
|
|
408
|
-
end
|
|
409
|
-
|
|
410
|
-
result[name] = value
|
|
411
|
-
end
|
|
412
|
-
|
|
413
|
-
extra = normalized.keys - self.class.parameters.keys
|
|
414
|
-
unless extra.empty?
|
|
415
|
-
return [nil, "unknown parameter(s): #{extra.inspect}"]
|
|
416
|
-
end
|
|
417
|
-
|
|
418
|
-
[result, nil]
|
|
419
|
-
end
|
|
420
|
-
|
|
421
|
-
def nested_schema_to_json_schema(nested)
|
|
422
|
-
nested.each_with_object({}) do |(prop_name, spec), result|
|
|
423
|
-
entry = {"type" => spec[:type].to_s}
|
|
424
|
-
entry["description"] = spec[:desc] if spec[:desc]
|
|
425
|
-
entry["enum"] = spec[:enum] if spec[:enum]
|
|
426
|
-
if spec[:properties]
|
|
427
|
-
entry["properties"] = nested_schema_to_json_schema(spec[:properties])
|
|
428
|
-
end
|
|
429
|
-
result[prop_name.to_s] = entry
|
|
430
|
-
end
|
|
431
|
-
end
|
|
432
|
-
|
|
433
|
-
def validate_nested_object(value, properties, path)
|
|
434
|
-
return "field '#{path}' must be an object (Hash)" unless value.is_a?(Hash)
|
|
435
|
-
|
|
436
|
-
normalized = value.transform_keys(&:to_sym)
|
|
437
|
-
extra = normalized.keys - properties.keys
|
|
438
|
-
unless extra.empty?
|
|
439
|
-
return "nested field '#{path}' contains undeclared key(s): #{extra.inspect}"
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
properties.each do |name, spec|
|
|
443
|
-
field_path = "#{path}.#{name}"
|
|
444
|
-
field_value = normalized[name]
|
|
445
|
-
|
|
446
|
-
if field_value.nil?
|
|
447
|
-
return "nested required field '#{field_path}' is missing" if spec[:required]
|
|
448
|
-
next
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
error = type_error(field_value, spec[:type])
|
|
452
|
-
return "nested field '#{field_path}': #{error}" if error
|
|
453
|
-
|
|
454
|
-
next unless spec[:type].to_sym == :object && spec[:properties]
|
|
455
|
-
|
|
456
|
-
error = validate_nested_object(field_value, spec[:properties], field_path)
|
|
457
|
-
return error if error
|
|
458
|
-
end
|
|
459
|
-
nil
|
|
460
|
-
end
|
|
461
|
-
|
|
462
|
-
def type_error(value, declared_type)
|
|
463
|
-
return nil if value.nil?
|
|
464
|
-
|
|
465
|
-
ok = case declared_type.to_sym
|
|
466
|
-
when :string then value.is_a?(String)
|
|
467
|
-
when :integer then value.is_a?(Integer)
|
|
468
|
-
when :number, :float then value.is_a?(Numeric)
|
|
469
|
-
when :boolean then [true, false].include?(value)
|
|
470
|
-
when :array then value.is_a?(Array)
|
|
471
|
-
when :object then value.is_a?(Hash)
|
|
472
|
-
else true
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
return nil if ok
|
|
476
|
-
|
|
477
|
-
shown = value.respond_to?(:keys) ? "(object)" : value.inspect
|
|
478
|
-
"parameter '#{shown}' expected type #{declared_type}"
|
|
479
|
-
end
|
|
480
|
-
|
|
481
|
-
def coerce_value(value, declared_type)
|
|
482
|
-
return [value, nil] if value.nil?
|
|
483
|
-
|
|
484
|
-
case declared_type.to_sym
|
|
485
|
-
when :string
|
|
486
|
-
[value.to_s, nil]
|
|
487
|
-
when :integer
|
|
488
|
-
[Integer(value), nil]
|
|
489
|
-
when :number, :float
|
|
490
|
-
[Float(value), nil]
|
|
491
|
-
when :boolean
|
|
492
|
-
case value.to_s.downcase
|
|
493
|
-
when "true" then [true, nil]
|
|
494
|
-
when "false" then [false, nil]
|
|
495
|
-
else [nil, "parameter cannot be coerced to boolean: #{value.inspect}"]
|
|
496
|
-
end
|
|
497
|
-
else
|
|
498
|
-
[value, nil]
|
|
499
|
-
end
|
|
500
|
-
rescue ArgumentError, TypeError
|
|
501
|
-
[nil, "parameter cannot be coerced to #{declared_type}: #{value.inspect}"]
|
|
502
|
-
end
|
|
503
|
-
end
|
|
504
|
-
end
|
|
505
|
-
end
|
|
506
|
-
end
|
|
507
|
-
end
|