phronomy 0.25.0 → 0.27.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/CHANGELOG.md +71 -0
- data/CONTRIBUTING.md +4 -4
- data/README.md +14 -7
- data/VERIFY.sh +27 -27
- data/benchmark/bench_agent_invoke.rb +26 -22
- data/benchmark/bench_context_assembler.rb +4 -5
- data/benchmark/bench_regression.rb +1 -1
- data/docs/architecture/agent-chat-and-state-ownership.md +147 -0
- data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
- data/docs/architecture/agent-context.md +5 -3
- data/docs/architecture/agent-transition-ownership.md +89 -0
- data/docs/architecture/before-llm-input.md +6 -0
- data/docs/architecture/context-management.md +30 -0
- data/docs/architecture/context-preparation-steps.md +90 -0
- data/docs/architecture/entry-action-and-team-wording.md +72 -0
- data/docs/architecture/execution-metadata-and-values.md +80 -0
- data/docs/architecture/generator-verifier-ownership.md +111 -0
- data/docs/architecture/multi-agent-handoff.md +8 -2
- data/docs/architecture/persistence-refactoring-plan.md +50 -0
- data/docs/architecture/persistence.md +29 -6
- data/docs/architecture/refactoring-closure.md +334 -0
- data/docs/architecture/remaining-refactoring-plan.md +374 -0
- data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
- data/docs/architecture/tool-schema-recording-gap.md +50 -0
- data/docs/architecture/tracing.md +4 -4
- data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
- data/docs/architecture.md +209 -0
- data/docs/async-composition.md +205 -0
- data/docs/decisions/010-cooperative-first-concurrency.md +23 -23
- data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
- 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/030-agent-handoff-domain-and-durable-responsibility.md +5 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +5 -0
- data/docs/decisions/032-storage-backend-composition.md +80 -0
- data/docs/decisions/033-domain-persistence-ownership.md +79 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +76 -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 +131 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +74 -0
- data/docs/decisions/040-configuration-default-composition.md +94 -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 +100 -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 +151 -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 +78 -0
- data/docs/decisions/README.md +35 -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/features.md +21 -12
- data/docs/getting-started.md +10 -10
- data/docs/migrations/0.15.md +5 -0
- data/docs/migrations/durable-semantic-coordination-v2.md +7 -1
- data/docs/migrations/handoff-runner-multi-agent.md +35 -0
- data/docs/migrations/neutral-storage-spi.md +43 -0
- data/docs/migrations/parallel-tool-chat-removal.md +49 -0
- data/docs/migrations/shared-state-multi-agent.md +44 -0
- data/docs/migrations/storage-backend-composition.md +117 -0
- data/docs/migrations/storage-transaction-boundaries.md +74 -0
- data/docs/persistence-backends.md +162 -204
- data/docs/runtime-and-concurrency.md +205 -60
- data/lib/phronomy/agent/api/agent.rb +17 -0
- data/lib/phronomy/agent/async_event_api.rb +2 -2
- data/lib/phronomy/agent/base.rb +55 -251
- data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
- data/lib/phronomy/agent/context/capability/base.rb +67 -26
- data/lib/phronomy/agent/context/capability/tool_executor.rb +62 -0
- data/lib/phronomy/agent/{context_assembler.rb → context_assembly/context_assembler.rb} +143 -88
- data/lib/phronomy/agent/{context_importer.rb → context_assembly/context_importer.rb} +2 -2
- data/lib/phronomy/agent/{ruby_llm_materializer.rb → context_assembly/ruby_llm_materializer.rb} +4 -7
- data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +36 -0
- data/lib/phronomy/agent/context_assembly/saved_context_reader.rb +53 -0
- data/lib/phronomy/agent/context_assembly/state_writer.rb +165 -0
- data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +22 -0
- data/lib/phronomy/agent/{context_plan.rb → context_contract/context_plan.rb} +1 -1
- data/lib/phronomy/agent/{context_policy_input.rb → context_contract/context_policy_input.rb} +6 -6
- data/lib/phronomy/agent/{llm_input_build_context.rb → context_contract/llm_input_build_context.rb} +1 -1
- data/lib/phronomy/agent/{llm_input_manifest.rb → context_contract/llm_input_manifest.rb} +20 -20
- data/lib/phronomy/agent/{llm_input_patch.rb → context_contract/llm_input_patch.rb} +2 -2
- data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
- data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +8 -13
- data/lib/phronomy/agent/{agent_invocation_session_builder.rb → execution/agent_invocation_session_builder.rb} +20 -96
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +305 -0
- data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +12 -13
- data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +2 -3
- data/lib/phronomy/agent/execution/execution_coordinator.rb +1925 -0
- data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
- data/lib/phronomy/agent/execution/execution_metadata.rb +53 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +344 -0
- data/lib/phronomy/agent/execution/execution_registry.rb +459 -0
- data/lib/phronomy/agent/execution/execution_session_runner.rb +118 -0
- data/lib/phronomy/agent/execution/initial_preparation.rb +421 -0
- data/lib/phronomy/agent/execution/invocation_transitions.rb +86 -0
- data/lib/phronomy/agent/{phase_machine_builder.rb → execution/phase_machine_builder.rb} +22 -73
- data/lib/phronomy/agent/{provider_call_outcome.rb → execution/provider_call_outcome.rb} +9 -9
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
- data/lib/phronomy/agent/{handoff_context.rb → handoff/handoff_context.rb} +2 -2
- data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +15 -0
- data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +131 -0
- data/lib/phronomy/agent/{handoff_state.rb → handoff/handoff_state.rb} +1 -1
- 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/{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 -10
- data/lib/phronomy/{agent_already_exists_error.rb → agent/lifecycle_contract/agent_already_exists_error.rb} +2 -0
- data/lib/phronomy/{agent_busy_error.rb → agent/lifecycle_contract/agent_busy_error.rb} +2 -0
- data/lib/phronomy/{agent_purged_error.rb → agent/lifecycle_contract/agent_purged_error.rb} +2 -0
- data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +7 -0
- data/lib/phronomy/{stream_callback_error.rb → agent/lifecycle_contract/stream_callback_error.rb} +2 -0
- data/lib/phronomy/agent/persistence/agent_repository.rb +61 -0
- data/lib/phronomy/agent/persistence/codec.rb +358 -0
- data/lib/phronomy/agent/persistence/execution_repository.rb +108 -0
- data/lib/phronomy/agent/persistence/handoff_state_repository.rb +58 -0
- data/lib/phronomy/agent/persistence/journal_repository.rb +54 -0
- data/lib/phronomy/agent/persistence/queries.rb +61 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +24 -0
- data/lib/phronomy/agent/persistence/watermark.rb +27 -0
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +132 -0
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +21 -45
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +36 -41
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +37 -40
- data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +9 -13
- data/lib/phronomy/agent/recovery/recovery_support.rb +227 -0
- data/lib/phronomy/agent/selection/candidate.rb +1 -1
- 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 +90 -0
- data/lib/phronomy/agent/{tool_call_intercepted.rb → tool_execution/tool_call_intercepted.rb} +2 -2
- data/lib/phronomy/agent/{tool_definition_set.rb → tool_execution/tool_definition_set.rb} +9 -4
- data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +69 -34
- data/lib/phronomy/agent/{tool_invocation_session_builder.rb → tool_execution/tool_invocation_session_builder.rb} +2 -2
- 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.rb → configuration/configuration.rb} +14 -5
- data/lib/phronomy/configuration/global_configuration.rb +26 -0
- data/lib/phronomy/content_store/storage_schema.rb +11 -0
- data/lib/phronomy/content_store/stored_contents.rb +43 -0
- data/lib/phronomy/engine/backpressure_error.rb +7 -0
- data/lib/phronomy/{blocking.rb → engine/blocking.rb} +15 -8
- data/lib/phronomy/engine/cancellation_error.rb +7 -0
- data/lib/phronomy/engine/concurrency/cancellation_token.rb +4 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +29 -16
- data/lib/phronomy/engine/concurrency/operation_binding.rb +43 -0
- data/lib/phronomy/engine/concurrency/physical_completion_task.rb +4 -62
- data/lib/phronomy/engine/concurrency/result_collector.rb +99 -0
- data/lib/phronomy/engine/concurrency/result_composition.rb +145 -0
- data/lib/phronomy/engine/concurrency/subscriptions.rb +68 -0
- data/lib/phronomy/engine/concurrency/worker_input_restricted.rb +14 -0
- data/lib/phronomy/engine/event_loop.rb +160 -626
- data/lib/phronomy/engine/event_loop_reentrancy_error.rb +8 -0
- data/lib/phronomy/engine/execution.rb +229 -0
- data/lib/phronomy/engine/execution_cancellation_error.rb +14 -0
- data/lib/phronomy/engine/execution_receiver.rb +65 -0
- data/lib/phronomy/engine/execution_timeout_error.rb +14 -0
- data/lib/phronomy/engine/fsm_protocol.rb +14 -0
- data/lib/phronomy/engine/fsm_session.rb +38 -31
- data/lib/phronomy/{invalid_async_entry_action_error.rb → engine/invalid_async_entry_action_error.rb} +3 -1
- data/lib/phronomy/{invalid_async_transition_action_error.rb → engine/invalid_async_transition_action_error.rb} +3 -1
- data/lib/phronomy/{invalid_async_workflow_action_error.rb → engine/invalid_async_workflow_action_error.rb} +3 -1
- data/lib/phronomy/{invocation_context.rb → engine/invocation_context.rb} +13 -1
- data/lib/phronomy/engine/pool_shutdown_error.rb +7 -0
- data/lib/phronomy/engine/recursion_limit_error.rb +7 -0
- data/lib/phronomy/engine/runtime/timer_queue.rb +11 -0
- data/lib/phronomy/engine/runtime.rb +80 -84
- data/lib/phronomy/engine/runtime_shutdown_error.rb +7 -0
- data/lib/phronomy/engine/runtime_shutdown_reentrancy_error.rb +7 -0
- data/lib/phronomy/engine/scheduler_reentrancy_error.rb +9 -0
- data/lib/phronomy/engine/{task.rb → task_result.rb} +101 -42
- data/lib/phronomy/engine/timeout_error.rb +7 -0
- data/lib/phronomy/filter/contract/filter_block_error.rb +14 -0
- data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -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/base.rb +2 -2
- data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
- data/lib/phronomy/llm_contract/authentication_error.rb +7 -0
- data/lib/phronomy/{context_budget_exceeded_error.rb → llm_contract/context_budget_exceeded_error.rb} +2 -0
- data/lib/phronomy/llm_contract/context_length_error.rb +7 -0
- data/lib/phronomy/llm_contract/rate_limit_error.rb +7 -0
- data/lib/phronomy/{token_usage.rb → llm_contract/token_usage.rb} +2 -2
- data/lib/phronomy/llm_contract/transport_error.rb +7 -0
- data/lib/phronomy/multi_agent/admission_registry.rb +22 -2
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +8 -8
- data/lib/phronomy/{agent → multi_agent}/handoff_runner.rb +18 -17
- data/lib/phronomy/multi_agent/orchestrator.rb +36 -68
- data/lib/phronomy/multi_agent/persistence/codec.rb +55 -0
- data/lib/phronomy/multi_agent/persistence/queries.rb +30 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +108 -0
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +61 -0
- data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
- data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +15 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +21 -18
- data/lib/phronomy/multi_agent/team_execution.rb +1 -1
- data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +14 -4
- data/lib/phronomy/multi_agent/team_root.rb +1 -1
- data/lib/phronomy/output_parser/contract/parse_error.rb +7 -0
- data/lib/phronomy/persistence/api/persistence.rb +140 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +19 -19
- data/lib/phronomy/persistence_composition/repositories.rb +77 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +24 -0
- data/lib/phronomy/{execution_rehydration_required_error.rb → recovery/execution_rehydration_required_error.rb} +2 -0
- data/lib/phronomy/{recovery.rb → recovery/recovery.rb} +1 -1
- data/lib/phronomy/runtime_composition/agent_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
- data/lib/phronomy/storage/backend.rb +101 -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/scorer/llm_judge.rb +5 -3
- data/lib/phronomy/testing/fake_clock.rb +13 -9
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +4 -4
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +10 -7
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +8 -8
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +123 -0
- data/lib/phronomy/testing/persistence_contract.rb +4 -0
- data/lib/phronomy/tool/contract/tool_error.rb +7 -0
- data/lib/phronomy/tools/agent.rb +6 -6
- data/lib/phronomy/vector_store/async_backend.rb +5 -5
- data/lib/phronomy/vector_store/embeddings/base.rb +2 -2
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
- data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +4 -0
- data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +188 -0
- data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +126 -75
- data/lib/phronomy/workflow/execution/workflow_terminal_policy.rb +40 -0
- data/lib/phronomy/workflow/persistence/codec.rb +153 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +57 -0
- data/lib/phronomy/workflow/phase_machine_builder.rb +8 -8
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +9 -0
- data/lib/phronomy.rb +52 -94
- data/scripts/api_snapshot.rb +4 -2
- data/scripts/storage_spi_snapshot.rb +36 -0
- data/sig/phronomy/agent.rbs +4 -5
- data/sig/phronomy/execution_receiver.rbs +34 -0
- data/sig/phronomy/extensions.rbs +5 -5
- data/sig/phronomy/handoff.rbs +4 -2
- data/sig/phronomy/multi_agent.rbs +19 -3
- data/sig/phronomy/persistence.rbs +7 -91
- data/sig/phronomy/runtime.rbs +34 -7
- data/sig/phronomy/storage.rbs +174 -0
- data/sig/phronomy/tool.rbs +11 -2
- data/sig/phronomy/workflow.rbs +1 -1
- metadata +236 -99
- data/lib/phronomy/agent/execution_coordinator.rb +0 -3138
- data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
- data/lib/phronomy/agent/recovery_support.rb +0 -504
- data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
- data/lib/phronomy/agent/tool_executor.rb +0 -55
- data/lib/phronomy/generator_verifier.rb +0 -369
- data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
- data/lib/phronomy/multi_agent/fan_out_invocation.rb +0 -137
- data/lib/phronomy/multi_agent/fan_out_session_builder.rb +0 -118
- 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/ruby_llm_patches.rb +0 -24
- data/lib/phronomy/workflow_recovery.rb +0 -123
- /data/lib/phronomy/agent/{context_candidate_resolver.rb → context_assembly/context_candidate_resolver.rb} +0 -0
- /data/lib/phronomy/agent/{context_policy_input_builder.rb → context_assembly/context_policy_input_builder.rb} +0 -0
- /data/lib/phronomy/agent/{context_plan_validator.rb → context_contract/context_plan_validator.rb} +0 -0
- /data/lib/phronomy/agent/{context_policy.rb → context_contract/context_policy.rb} +0 -0
- /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
- /data/lib/phronomy/agent/{handoff.rb → handoff/handoff.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_capability_factory.rb → handoff/handoff_capability_factory.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_policy.rb → handoff/handoff_policy.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_projection.rb → handoff/handoff_projection.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_request.rb → handoff/handoff_request.rb} +0 -0
- /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
- /data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +0 -0
- /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
- /data/lib/phronomy/{metrics.rb → engine/metrics.rb} +0 -0
- /data/lib/phronomy/{runnable.rb → engine/runnable.rb} +0 -0
data/lib/phronomy/agent/base.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../engine/concurrency/worker_input_restricted"
|
|
4
|
+
|
|
3
5
|
require "securerandom"
|
|
4
6
|
require_relative "concerns/filterable"
|
|
5
7
|
require_relative "concerns/before_llm_input"
|
|
@@ -31,6 +33,8 @@ module Phronomy
|
|
|
31
33
|
# max_iterations 15
|
|
32
34
|
# end
|
|
33
35
|
class Base
|
|
36
|
+
include Phronomy::Concurrency::WorkerInputRestricted
|
|
37
|
+
|
|
34
38
|
include Phronomy::Runnable
|
|
35
39
|
include Concerns::Filterable
|
|
36
40
|
include Concerns::BeforeLLMInput
|
|
@@ -119,15 +123,9 @@ module Phronomy
|
|
|
119
123
|
if val.nil?
|
|
120
124
|
@max_output_tokens
|
|
121
125
|
else
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def context_window(val = nil)
|
|
127
|
-
if val.nil?
|
|
128
|
-
@context_window
|
|
129
|
-
else
|
|
130
|
-
@context_window = val.to_i
|
|
126
|
+
value = Integer(val)
|
|
127
|
+
raise ArgumentError, "max_output_tokens must be positive" unless value.positive?
|
|
128
|
+
@max_output_tokens = value
|
|
131
129
|
end
|
|
132
130
|
end
|
|
133
131
|
|
|
@@ -204,7 +202,7 @@ module Phronomy
|
|
|
204
202
|
runtime = Phronomy::Runtime.instance
|
|
205
203
|
materialized = false
|
|
206
204
|
|
|
207
|
-
agent = runtime.
|
|
205
|
+
agent = OwnershipRegistry.for(runtime).load(key, expected_class: self) do |owner_runtime|
|
|
208
206
|
materialized = true
|
|
209
207
|
instance = __construct_owned_agent(
|
|
210
208
|
owner_runtime,
|
|
@@ -235,14 +233,17 @@ module Phronomy
|
|
|
235
233
|
# Returns only the process-local live Agent owner. Does not access
|
|
236
234
|
# Persistence and returns nil when this Runtime has no live owner.
|
|
237
235
|
def get(agent_id)
|
|
238
|
-
|
|
236
|
+
key = agent_id.to_s
|
|
237
|
+
raise ArgumentError, "agent_id must not be empty" if key.empty?
|
|
238
|
+
|
|
239
|
+
OwnershipRegistry.existing_for(Phronomy::Runtime.instance)&.get(key, expected_class: self)
|
|
239
240
|
end
|
|
240
241
|
|
|
241
242
|
# Resolves the live Agent instance that currently owns execution_id in
|
|
242
243
|
# this process. The Runtime returns only a read-only ownership view;
|
|
243
244
|
# mutable Agent execution state remains EventLoop-owned.
|
|
244
245
|
def live_for_execution(execution_id)
|
|
245
|
-
owner = Phronomy::Runtime.instance
|
|
246
|
+
owner = Phronomy::Agent::ExecutionRegistry.existing_for(Phronomy::Runtime.instance)&.agent_execution_owner(execution_id)
|
|
246
247
|
unless owner
|
|
247
248
|
raise Phronomy::ExecutionRehydrationRequiredError,
|
|
248
249
|
"no live execution owner for #{execution_id}; durable rehydration is required"
|
|
@@ -338,7 +339,7 @@ module Phronomy
|
|
|
338
339
|
|
|
339
340
|
runtime = Phronomy::Runtime.instance
|
|
340
341
|
begin
|
|
341
|
-
runtime.
|
|
342
|
+
OwnershipRegistry.for(runtime).create(effective_agent_id, expected_class: self.class) do |owner_runtime|
|
|
342
343
|
__prepare_runtime_owner!(owner_runtime, effective_agent_id)
|
|
343
344
|
initialize_owned_state(
|
|
344
345
|
agent_id: effective_agent_id,
|
|
@@ -350,7 +351,7 @@ module Phronomy
|
|
|
350
351
|
)
|
|
351
352
|
self
|
|
352
353
|
end
|
|
353
|
-
rescue Phronomy::
|
|
354
|
+
rescue Phronomy::Storage::ConflictError => error
|
|
354
355
|
raise Phronomy::AgentAlreadyExistsError,
|
|
355
356
|
"Agent #{effective_agent_id.inspect} already exists durably: #{error.message}"
|
|
356
357
|
end
|
|
@@ -395,34 +396,8 @@ module Phronomy
|
|
|
395
396
|
def add_knowledge(content, metadata: {})
|
|
396
397
|
__assert_live_agent!
|
|
397
398
|
current = agent_root
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
persistence.transaction do |tx|
|
|
401
|
-
tx.executions.assert_idle!(agent_id)
|
|
402
|
-
record = build_knowledge_record(
|
|
403
|
-
tx: tx,
|
|
404
|
-
root: current,
|
|
405
|
-
content: content,
|
|
406
|
-
metadata: metadata
|
|
407
|
-
)
|
|
408
|
-
appended = tx.journals.append(
|
|
409
|
-
agent_id,
|
|
410
|
-
expected_position: current.journal_position,
|
|
411
|
-
records: [record]
|
|
412
|
-
)
|
|
413
|
-
next_root = current.with(
|
|
414
|
-
agent_revision: current.agent_revision + 1,
|
|
415
|
-
context_revision: current.context_revision + 1,
|
|
416
|
-
journal_position: current.journal_position + appended.length
|
|
417
|
-
)
|
|
418
|
-
tx.agents.save(
|
|
419
|
-
agent_id,
|
|
420
|
-
expected_revision: current.agent_revision,
|
|
421
|
-
root: next_root
|
|
422
|
-
)
|
|
423
|
-
end
|
|
424
|
-
_append_journal_records(appended)
|
|
425
|
-
@root = next_root
|
|
399
|
+
update = state_writer.add_knowledge(root: current, content: content, metadata: metadata)
|
|
400
|
+
apply_state_update(update)
|
|
426
401
|
self
|
|
427
402
|
end
|
|
428
403
|
|
|
@@ -450,9 +425,10 @@ module Phronomy
|
|
|
450
425
|
|
|
451
426
|
__assert_live_agent!
|
|
452
427
|
runtime = @_phronomy_runtime_owner
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
428
|
+
registry = OwnershipRegistry.for(runtime)
|
|
429
|
+
token = registry.begin_purge(self)
|
|
430
|
+
if Phronomy::Agent::ExecutionRegistry.existing_for(runtime)&.agent_execution_admitted?(agent_id)
|
|
431
|
+
registry.abort_purge(self, token)
|
|
456
432
|
raise Phronomy::AgentBusyError,
|
|
457
433
|
"Agent #{agent_id.inspect} has a nonterminal top-level execution"
|
|
458
434
|
end
|
|
@@ -471,20 +447,20 @@ module Phronomy
|
|
|
471
447
|
tx.agents.delete(agent_id)
|
|
472
448
|
end
|
|
473
449
|
rescue Phronomy::AgentBusyError,
|
|
474
|
-
Phronomy::
|
|
475
|
-
Phronomy::
|
|
476
|
-
Phronomy::
|
|
450
|
+
Phronomy::Storage::ConflictError,
|
|
451
|
+
Phronomy::Storage::NotFoundError,
|
|
452
|
+
Phronomy::Storage::SerializationError,
|
|
477
453
|
ArgumentError
|
|
478
|
-
|
|
454
|
+
registry.abort_purge(self, token)
|
|
479
455
|
raise
|
|
480
456
|
rescue
|
|
481
457
|
# Without F1 reconciliation support we cannot infer that a failed
|
|
482
458
|
# durable delete did not commit. Keep the identity fail-closed.
|
|
483
|
-
|
|
459
|
+
registry.leave_purge_uncertain(self, token)
|
|
484
460
|
raise
|
|
485
461
|
end
|
|
486
462
|
|
|
487
|
-
|
|
463
|
+
registry.complete_purge(self, token)
|
|
488
464
|
true
|
|
489
465
|
end
|
|
490
466
|
|
|
@@ -585,7 +561,7 @@ module Phronomy
|
|
|
585
561
|
)
|
|
586
562
|
@persistence = persistence ||
|
|
587
563
|
Phronomy.configuration.persistence ||
|
|
588
|
-
|
|
564
|
+
DefaultPersistence.build
|
|
589
565
|
@agent_id = agent_id.to_s.freeze
|
|
590
566
|
|
|
591
567
|
if load_existing
|
|
@@ -630,117 +606,27 @@ module Phronomy
|
|
|
630
606
|
|
|
631
607
|
def create_agent_root!(context:, knowledge:, metadata:)
|
|
632
608
|
definition = self.class.agent_definition
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
agent_definition_id: definition.fetch(:id),
|
|
636
|
-
agent_definition_version: definition.fetch(:version),
|
|
637
|
-
metadata: metadata
|
|
638
|
-
)
|
|
639
|
-
persistence.transaction do |tx|
|
|
640
|
-
tx.agents.create(root)
|
|
641
|
-
records = initial_context_records(tx: tx, root: root, context: context)
|
|
642
|
-
records.concat(initial_knowledge_records(tx: tx, root: root, knowledge: knowledge))
|
|
643
|
-
unless records.empty?
|
|
644
|
-
appended = tx.journals.append(agent_id, expected_position: 0, records: records)
|
|
645
|
-
root = root.with(
|
|
646
|
-
agent_revision: 1,
|
|
647
|
-
context_revision: 1,
|
|
648
|
-
journal_position: appended.length
|
|
649
|
-
)
|
|
650
|
-
tx.agents.save(agent_id, expected_revision: 0, root: root)
|
|
651
|
-
end
|
|
652
|
-
end
|
|
653
|
-
root
|
|
654
|
-
end
|
|
655
|
-
|
|
656
|
-
def initial_context_records(tx:, root:, context:)
|
|
657
|
-
return [] unless context
|
|
658
|
-
|
|
659
|
-
imported = context.respond_to?(:records) ? context :
|
|
660
|
-
Agent::ContextImporter.import_messages(context)
|
|
661
|
-
imported.records.map do |record|
|
|
662
|
-
content_ref = case record.content_format
|
|
663
|
-
when :text then tx.contents.put_text(record.content)
|
|
664
|
-
when :json then tx.contents.put_json(record.content)
|
|
665
|
-
else
|
|
666
|
-
raise ArgumentError,
|
|
667
|
-
"unsupported imported content format: #{record.content_format.inspect}"
|
|
668
|
-
end
|
|
669
|
-
Agent::JournalRecord.new(
|
|
670
|
-
agent_id: agent_id,
|
|
671
|
-
kind: record.kind,
|
|
672
|
-
channel: record.channel,
|
|
673
|
-
role: record.role,
|
|
674
|
-
content_ref: content_ref,
|
|
675
|
-
context_generation: root.transcript_generation,
|
|
676
|
-
context_candidate: true,
|
|
677
|
-
metadata: record.metadata
|
|
678
|
-
)
|
|
679
|
-
end
|
|
680
|
-
end
|
|
681
|
-
|
|
682
|
-
def initial_knowledge_records(tx:, root:, knowledge:)
|
|
683
|
-
Array(knowledge).map do |content|
|
|
684
|
-
build_knowledge_record(
|
|
685
|
-
tx: tx,
|
|
686
|
-
root: root,
|
|
687
|
-
content: content,
|
|
688
|
-
metadata: {}
|
|
689
|
-
)
|
|
690
|
-
end
|
|
691
|
-
end
|
|
692
|
-
|
|
693
|
-
def build_knowledge_record(tx:, root:, content:, metadata:)
|
|
694
|
-
Agent::JournalRecord.new(
|
|
695
|
-
agent_id: agent_id,
|
|
696
|
-
kind: :knowledge,
|
|
697
|
-
channel: :context,
|
|
698
|
-
role: :user,
|
|
699
|
-
content_ref: tx.contents.put_text(String(content)),
|
|
700
|
-
context_generation: root.transcript_generation,
|
|
701
|
-
context_candidate: true,
|
|
702
|
-
metadata: metadata || {}
|
|
609
|
+
state_writer.create_root(
|
|
610
|
+
definition: definition, context: context, knowledge: knowledge, metadata: metadata
|
|
703
611
|
)
|
|
704
612
|
end
|
|
705
613
|
|
|
706
614
|
def mutate_context!(kind, context_affecting: true)
|
|
707
615
|
__assert_live_agent!
|
|
708
616
|
current = agent_root
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
context_generation: current.transcript_generation,
|
|
718
|
-
context_candidate: false
|
|
719
|
-
)
|
|
720
|
-
appended = tx.journals.append(
|
|
721
|
-
agent_id,
|
|
722
|
-
expected_position: current.journal_position,
|
|
723
|
-
records: [record]
|
|
724
|
-
)
|
|
725
|
-
proposed = yield(current)
|
|
726
|
-
next_root = proposed.with(
|
|
727
|
-
journal_position: current.journal_position + appended.length,
|
|
728
|
-
context_revision: context_affecting ?
|
|
729
|
-
yield_context_revision(current, proposed) : current.context_revision
|
|
730
|
-
)
|
|
731
|
-
tx.agents.save(
|
|
732
|
-
agent_id,
|
|
733
|
-
expected_revision: current.agent_revision,
|
|
734
|
-
root: next_root
|
|
735
|
-
)
|
|
736
|
-
end
|
|
737
|
-
_append_journal_records(appended)
|
|
738
|
-
@root = next_root
|
|
617
|
+
update = state_writer.mutate_context(
|
|
618
|
+
root: current, kind: kind, context_affecting: context_affecting
|
|
619
|
+
) { |root| yield(root) }
|
|
620
|
+
apply_state_update(update)
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
def state_writer
|
|
624
|
+
StateWriter.new(persistence: persistence, agent_id: agent_id)
|
|
739
625
|
end
|
|
740
626
|
|
|
741
|
-
def
|
|
742
|
-
(
|
|
743
|
-
|
|
627
|
+
def apply_state_update(update)
|
|
628
|
+
_append_journal_records(update.records)
|
|
629
|
+
@root = update.root
|
|
744
630
|
end
|
|
745
631
|
|
|
746
632
|
def _journal_records_snapshot
|
|
@@ -789,7 +675,8 @@ module Phronomy
|
|
|
789
675
|
return true if @_phronomy_runtime_owner_state == :constructing
|
|
790
676
|
|
|
791
677
|
runtime = @_phronomy_runtime_owner
|
|
792
|
-
|
|
678
|
+
registry = OwnershipRegistry.existing_for(runtime) if runtime
|
|
679
|
+
unless registry&.owned?(self)
|
|
793
680
|
raise Phronomy::RuntimeShutdownError,
|
|
794
681
|
"Agent #{agent_id.inspect} is no longer the live owner in its Runtime"
|
|
795
682
|
end
|
|
@@ -799,18 +686,12 @@ module Phronomy
|
|
|
799
686
|
def _prepare_invocation_config(config, invocation_context)
|
|
800
687
|
__assert_live_agent!
|
|
801
688
|
_reject_removed_generic_identity_keys!(config)
|
|
802
|
-
|
|
689
|
+
invocation_context ?
|
|
803
690
|
config.merge(invocation_context: invocation_context) : config
|
|
804
691
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
cancellation_token: tok,
|
|
809
|
-
phronomy_timeout_deadline: invocation_context.deadline
|
|
810
|
-
)
|
|
811
|
-
end
|
|
812
|
-
end
|
|
813
|
-
effective_config
|
|
692
|
+
# ExecutionCoordinator connects context and individual controls together
|
|
693
|
+
# at admission, before returning the result. Do not arm a second timer
|
|
694
|
+
# here or replace one cancellation source with another.
|
|
814
695
|
end
|
|
815
696
|
|
|
816
697
|
def _reject_removed_generic_identity_keys!(config)
|
|
@@ -949,7 +830,7 @@ module Phronomy
|
|
|
949
830
|
)
|
|
950
831
|
end
|
|
951
832
|
projection.tool_classes.each do |tool_class|
|
|
952
|
-
chat.
|
|
833
|
+
chat.with_tools(prepare_tool_class(tool_class, invocation: invocation))
|
|
953
834
|
end
|
|
954
835
|
projection.messages.each { |message| chat.messages << message }
|
|
955
836
|
chat
|
|
@@ -960,25 +841,9 @@ module Phronomy
|
|
|
960
841
|
"model" => self.class.model,
|
|
961
842
|
"provider" => self.class.provider,
|
|
962
843
|
"temperature" => self.class.temperature,
|
|
963
|
-
"max_output_tokens" => self.class.max_output_tokens
|
|
964
|
-
"parallel_tool_execution" => Phronomy.configuration.parallel_tool_execution
|
|
844
|
+
"max_output_tokens" => self.class.max_output_tokens
|
|
965
845
|
}
|
|
966
|
-
|
|
967
|
-
model = config["model"]
|
|
968
|
-
opts[:model] = model if model
|
|
969
|
-
provider = config["provider"]
|
|
970
|
-
if provider
|
|
971
|
-
opts[:provider] = provider.to_sym
|
|
972
|
-
opts[:assume_model_exists] = true
|
|
973
|
-
end
|
|
974
|
-
parallel_class = config["parallel_tool_execution"] ?
|
|
975
|
-
Phronomy::MultiAgent::ParallelToolChat : nil
|
|
976
|
-
chat = parallel_class ? parallel_class.new(**opts) : RubyLLM.chat(**opts)
|
|
977
|
-
chat.with_temperature(config["temperature"]) if config["temperature"]
|
|
978
|
-
if config["max_output_tokens"] && chat.respond_to?(:with_max_output_tokens)
|
|
979
|
-
chat.with_max_output_tokens(config["max_output_tokens"])
|
|
980
|
-
end
|
|
981
|
-
chat
|
|
846
|
+
RuntimeChatBuilder.build(config)
|
|
982
847
|
end
|
|
983
848
|
|
|
984
849
|
def build_instructions(input)
|
|
@@ -994,12 +859,7 @@ module Phronomy
|
|
|
994
859
|
end
|
|
995
860
|
|
|
996
861
|
def apply_instructions(chat, text, cache: false, provider: nil)
|
|
997
|
-
|
|
998
|
-
content = RubyLLM::Providers::Anthropic::Content.new(text, cache: true)
|
|
999
|
-
chat.with_instructions(content)
|
|
1000
|
-
else
|
|
1001
|
-
chat.with_instructions(text)
|
|
1002
|
-
end
|
|
862
|
+
RuntimeChatBuilder.apply_instructions(chat, text, cache: cache, provider: provider)
|
|
1003
863
|
end
|
|
1004
864
|
|
|
1005
865
|
def extract_message(input)
|
|
@@ -1027,66 +887,10 @@ module Phronomy
|
|
|
1027
887
|
def prepare_tool_class(tool_class, invocation: nil)
|
|
1028
888
|
return tool_class unless tool_class.is_a?(Class)
|
|
1029
889
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
else
|
|
1035
|
-
tool_class
|
|
1036
|
-
end
|
|
1037
|
-
|
|
1038
|
-
result_filters = _tool_result_filters_for(tool_class)
|
|
1039
|
-
return resolved if result_filters.empty?
|
|
1040
|
-
|
|
1041
|
-
effective_name = resolved.new.name
|
|
1042
|
-
custom_async_call =
|
|
1043
|
-
resolved.instance_method(:call_async).owner !=
|
|
1044
|
-
Phronomy::Agent::Context::Capability::Base
|
|
1045
|
-
|
|
1046
|
-
Class.new(resolved) do
|
|
1047
|
-
tool_name effective_name
|
|
1048
|
-
define_method(:call) do |args, **kwargs|
|
|
1049
|
-
result = super(args, **kwargs)
|
|
1050
|
-
result_filters.inject(result) { |val, filter|
|
|
1051
|
-
filter.call(val, tool_name: name, args: args)
|
|
1052
|
-
}
|
|
1053
|
-
end
|
|
1054
|
-
|
|
1055
|
-
if custom_async_call
|
|
1056
|
-
define_method(:call_async) do |args, **kwargs|
|
|
1057
|
-
source = super(args, **kwargs)
|
|
1058
|
-
filtered = Phronomy::Concurrency::PhysicalCompletionTask.deferred(
|
|
1059
|
-
name: "tool-filter-#{name}"
|
|
1060
|
-
)
|
|
1061
|
-
source_has_physical_signal = source.respond_to?(:on_physical_complete)
|
|
1062
|
-
source.on_physical_complete { filtered.mark_physical_complete! } if
|
|
1063
|
-
source_has_physical_signal
|
|
1064
|
-
source.on_complete do |value, error|
|
|
1065
|
-
if error
|
|
1066
|
-
filtered.mark_physical_complete! unless source_has_physical_signal
|
|
1067
|
-
if source.respond_to?(:status) && source.status == :cancelled
|
|
1068
|
-
filtered.cancel!(error)
|
|
1069
|
-
else
|
|
1070
|
-
filtered.fail(error)
|
|
1071
|
-
end
|
|
1072
|
-
next
|
|
1073
|
-
end
|
|
1074
|
-
|
|
1075
|
-
begin
|
|
1076
|
-
result = result_filters.inject(value) { |val, filter|
|
|
1077
|
-
filter.call(val, tool_name: name, args: args)
|
|
1078
|
-
}
|
|
1079
|
-
filtered.mark_physical_complete! unless source_has_physical_signal
|
|
1080
|
-
filtered.complete(result)
|
|
1081
|
-
rescue => filter_error
|
|
1082
|
-
filtered.mark_physical_complete! unless source_has_physical_signal
|
|
1083
|
-
filtered.fail(filter_error)
|
|
1084
|
-
end
|
|
1085
|
-
end
|
|
1086
|
-
filtered
|
|
1087
|
-
end
|
|
1088
|
-
end
|
|
1089
|
-
end
|
|
890
|
+
binding = ToolBinding.new(
|
|
891
|
+
tool_class, alias_name: self.class.tool_aliases[tool_class]
|
|
892
|
+
)
|
|
893
|
+
binding.prepare(result_filters: _tool_result_filters_for(tool_class))
|
|
1090
894
|
end
|
|
1091
895
|
end
|
|
1092
896
|
end
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# This convenience API composes a fresh ephemeral Persistence with an Agent.
|
|
4
|
+
# The application entry loads the method definition; Agent execution does not
|
|
5
|
+
# delegate to or require this higher-level composition implementation.
|
|
3
6
|
module Phronomy
|
|
4
7
|
module Agent
|
|
5
|
-
StreamEvent = Data.define(:type, :payload)
|
|
6
|
-
|
|
7
8
|
def self.run_once(
|
|
8
9
|
definition:,
|
|
9
10
|
input:,
|
|
@@ -17,7 +18,7 @@ module Phronomy
|
|
|
17
18
|
raise ArgumentError, "Provide either on_event: or a block, not both"
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
persistence = Phronomy::Persistence
|
|
21
|
+
persistence = Phronomy::Persistence.in_memory
|
|
21
22
|
agent = definition.create(
|
|
22
23
|
context: context,
|
|
23
24
|
knowledge: knowledge,
|
|
@@ -29,13 +30,3 @@ module Phronomy
|
|
|
29
30
|
end
|
|
30
31
|
end
|
|
31
32
|
end
|
|
32
|
-
|
|
33
|
-
require_relative "agent/async_event_api"
|
|
34
|
-
|
|
35
|
-
unless Phronomy::Agent::Base < Phronomy::Agent::AsyncEventApi
|
|
36
|
-
Phronomy::Agent::Base.prepend(Phronomy::Agent::AsyncEventApi)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Durable Agent Recovery and Agent-incarnation event binding.
|
|
40
|
-
require_relative "agent/recovery_support"
|
|
41
|
-
require_relative "agent/recovery_coordinator"
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../../../engine/concurrency/worker_input_restricted"
|
|
4
|
+
|
|
3
5
|
module Phronomy
|
|
4
6
|
module Agent
|
|
5
7
|
module Context
|
|
6
8
|
module Capability
|
|
7
9
|
# Base class extending RubyLLM::Tool with Phronomy-specific DSL.
|
|
8
10
|
class Base < RubyLLM::Tool
|
|
11
|
+
include Phronomy::Concurrency::WorkerInputRestricted
|
|
12
|
+
|
|
9
13
|
class << self
|
|
10
14
|
# @api public
|
|
11
15
|
def tool_name(value = nil)
|
|
12
|
-
return @tool_name if value.nil?
|
|
16
|
+
return @tool_name || super() if value.nil?
|
|
13
17
|
|
|
14
18
|
@tool_name = value.to_s
|
|
15
19
|
end
|
|
@@ -30,41 +34,62 @@ module Phronomy
|
|
|
30
34
|
end
|
|
31
35
|
alias_method :desc, :description
|
|
32
36
|
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
37
|
+
# Phronomy declarations inherit through anonymous Tool decorators.
|
|
38
|
+
# RubyLLM owns schema construction and provider request rendering.
|
|
39
|
+
# @api public
|
|
40
|
+
def declared_parameters
|
|
41
|
+
return @declared_parameters if instance_variable_defined?(:@declared_parameters)
|
|
42
|
+
|
|
43
|
+
@declared_parameters = superclass.declared_parameters.dup
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Keep Phronomy's parameter reader and support RubyLLM's schema DSL.
|
|
36
47
|
# @api public
|
|
37
|
-
def parameters
|
|
38
|
-
return
|
|
48
|
+
def parameters(schema = :__phronomy_read__, &block)
|
|
49
|
+
return declared_parameters if schema == :__phronomy_read__ && !block
|
|
39
50
|
|
|
40
|
-
|
|
41
|
-
|
|
51
|
+
super((schema == :__phronomy_read__) ? nil : schema, &block)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @api public
|
|
55
|
+
def parameters_schema_definition
|
|
56
|
+
return @parameters_schema_definition if instance_variable_defined?(:@parameters_schema_definition)
|
|
57
|
+
|
|
58
|
+
superclass.parameters_schema_definition
|
|
42
59
|
end
|
|
43
60
|
|
|
44
|
-
# RubyLLM stores an explicit .params schema definition in a
|
|
45
|
-
# class-instance variable. Readers must fall back to the parent.
|
|
46
61
|
# @api public
|
|
47
62
|
def params_schema_definition
|
|
48
|
-
|
|
49
|
-
|
|
63
|
+
parameters_schema_definition
|
|
64
|
+
end
|
|
50
65
|
|
|
51
|
-
|
|
66
|
+
# @api public
|
|
67
|
+
def params(schema = nil, &block)
|
|
68
|
+
parameters(schema, &block)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @api public
|
|
72
|
+
def provider_options(options = :__phronomy_read__)
|
|
73
|
+
return super unless options == :__phronomy_read__
|
|
74
|
+
return @provider_options if instance_variable_defined?(:@provider_options)
|
|
75
|
+
|
|
76
|
+
@provider_options = duplicate_configuration(superclass.provider_options)
|
|
52
77
|
end
|
|
53
78
|
|
|
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
79
|
# @api public
|
|
58
80
|
def provider_params
|
|
59
|
-
|
|
81
|
+
provider_options
|
|
82
|
+
end
|
|
60
83
|
|
|
61
|
-
|
|
62
|
-
|
|
84
|
+
# @api public
|
|
85
|
+
def with_params(**options)
|
|
86
|
+
provider_options(options)
|
|
63
87
|
end
|
|
64
88
|
|
|
65
89
|
# @api public
|
|
66
|
-
def param(name, enum: nil, properties: nil, **options)
|
|
67
|
-
|
|
90
|
+
def param(name, enum: nil, properties: nil, desc: nil, **options)
|
|
91
|
+
options[:description] = desc if desc
|
|
92
|
+
parameter(name, **options)
|
|
68
93
|
param_enums[name] = duplicate_configuration(enum) if enum
|
|
69
94
|
param_schemas[name] = normalize_nested_schema(properties) if properties
|
|
70
95
|
end
|
|
@@ -175,8 +200,8 @@ module Phronomy
|
|
|
175
200
|
end
|
|
176
201
|
@requires_approval = block
|
|
177
202
|
elsif value == :__unset__
|
|
178
|
-
return @requires_approval
|
|
179
|
-
return superclass.requires_approval if superclass
|
|
203
|
+
return @requires_approval unless @requires_approval.nil?
|
|
204
|
+
return superclass.requires_approval if superclass < RubyLLM::Tool
|
|
180
205
|
|
|
181
206
|
false
|
|
182
207
|
else
|
|
@@ -225,7 +250,7 @@ module Phronomy
|
|
|
225
250
|
self.class.tool_name || super
|
|
226
251
|
end
|
|
227
252
|
|
|
228
|
-
def
|
|
253
|
+
def parameters_schema
|
|
229
254
|
schema = super
|
|
230
255
|
return schema if schema.nil?
|
|
231
256
|
|
|
@@ -264,6 +289,22 @@ module Phronomy
|
|
|
264
289
|
schema
|
|
265
290
|
end
|
|
266
291
|
|
|
292
|
+
# Phronomy retains these readers as part of its Tool contract.
|
|
293
|
+
# @api public
|
|
294
|
+
def params_schema
|
|
295
|
+
parameters_schema
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# @api public
|
|
299
|
+
def provider_params
|
|
300
|
+
provider_options
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# @api public
|
|
304
|
+
def parameters
|
|
305
|
+
self.class.declared_parameters
|
|
306
|
+
end
|
|
307
|
+
|
|
267
308
|
# @api public
|
|
268
309
|
def call(args, cancellation_token: nil)
|
|
269
310
|
cancellation_token&.raise_if_cancelled!
|
|
@@ -281,7 +322,7 @@ module Phronomy
|
|
|
281
322
|
if cancellation_token && execute_accepts_cancellation_token?
|
|
282
323
|
validated_args = validated_args.merge(cancellation_token: cancellation_token)
|
|
283
324
|
end
|
|
284
|
-
result = super(validated_args)
|
|
325
|
+
result = super(**(validated_args || {}).transform_keys(&:to_sym))
|
|
285
326
|
truncate_result_if_needed(result)
|
|
286
327
|
rescue Phronomy::ToolError, Phronomy::CancellationError
|
|
287
328
|
raise
|
|
@@ -307,7 +348,7 @@ module Phronomy
|
|
|
307
348
|
cancellation_token: nil,
|
|
308
349
|
config: {}
|
|
309
350
|
)
|
|
310
|
-
Phronomy::Agent::ToolExecutor.call_async(
|
|
351
|
+
Phronomy::Agent::Context::Capability::ToolExecutor.call_async(
|
|
311
352
|
tool: self,
|
|
312
353
|
args: args,
|
|
313
354
|
cancellation_token: cancellation_token,
|