phronomy 0.26.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 +40 -0
- data/README.md +4 -0
- data/VERIFY.sh +27 -27
- data/benchmark/bench_agent_invoke.rb +26 -22
- data/benchmark/bench_context_assembler.rb +4 -5
- 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 +4 -2
- 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 +27 -4
- 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 +1 -1
- data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
- data/docs/architecture.md +209 -0
- 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 +9 -3
- data/docs/getting-started.md +4 -4
- data/docs/migrations/0.15.md +5 -0
- data/docs/migrations/durable-semantic-coordination-v2.md +6 -0
- 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 +117 -32
- 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 +51 -241
- 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} +19 -95
- 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} +9 -10
- 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} +20 -71
- 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 +32 -37
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +37 -40
- data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +6 -10
- 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/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} +1 -1
- 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 +4 -0
- data/lib/phronomy/engine/concurrency/operation_binding.rb +1 -1
- data/lib/phronomy/engine/concurrency/worker_input_restricted.rb +14 -0
- data/lib/phronomy/engine/event_loop.rb +158 -624
- data/lib/phronomy/engine/event_loop_reentrancy_error.rb +8 -0
- data/lib/phronomy/{execution.rb → engine/execution.rb} +0 -7
- data/lib/phronomy/{execution_cancellation_error.rb → engine/execution_cancellation_error.rb} +2 -0
- data/lib/phronomy/engine/execution_receiver.rb +65 -0
- data/lib/phronomy/{execution_timeout_error.rb → engine/execution_timeout_error.rb} +2 -0
- data/lib/phronomy/engine/fsm_protocol.rb +14 -0
- data/lib/phronomy/engine/fsm_session.rb +35 -28
- data/lib/phronomy/{invalid_async_entry_action_error.rb → engine/invalid_async_entry_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_transition_action_error.rb → engine/invalid_async_transition_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_workflow_action_error.rb → engine/invalid_async_workflow_action_error.rb} +2 -0
- 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.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_result.rb +4 -0
- 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_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 +5 -5
- data/lib/phronomy/{agent → multi_agent}/handoff_runner.rb +18 -17
- data/lib/phronomy/multi_agent/orchestrator.rb +2 -2
- 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/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 +1 -1
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +8 -6
- 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} +122 -71
- 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 +2 -2
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +9 -0
- data/lib/phronomy.rb +52 -94
- data/scripts/api_snapshot.rb +1 -1
- data/scripts/storage_spi_snapshot.rb +36 -0
- data/sig/phronomy/agent.rbs +0 -1
- data/sig/phronomy/execution_receiver.rbs +34 -0
- data/sig/phronomy/handoff.rbs +4 -2
- data/sig/phronomy/multi_agent.rbs +18 -0
- data/sig/phronomy/persistence.rbs +7 -91
- data/sig/phronomy/storage.rbs +174 -0
- data/sig/phronomy/tool.rbs +10 -1
- metadata +230 -99
- data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
- 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/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/agent/{tool_invocation_session_builder.rb → tool_execution/tool_invocation_session_builder.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/{invocation_context.rb → engine/invocation_context.rb} +0 -0
- /data/lib/phronomy/{metrics.rb → engine/metrics.rb} +0 -0
- /data/lib/phronomy/{runnable.rb → engine/runnable.rb} +0 -0
|
@@ -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,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
module Context
|
|
6
|
+
module Capability
|
|
7
|
+
# Routes Tool work according to the Tool execution contract.
|
|
8
|
+
#
|
|
9
|
+
# Both execution modes return {Phronomy::TaskResult}; only the execution mechanism
|
|
10
|
+
# differs.
|
|
11
|
+
#
|
|
12
|
+
# :cooperative Tool calls execute inline and must return quickly. call_async
|
|
13
|
+
# wraps their result in an already-settled TaskResult and never consumes an
|
|
14
|
+
# OffloadPool worker.
|
|
15
|
+
#
|
|
16
|
+
# :offloaded Tool calls route synchronous work through OffloadPool, whose
|
|
17
|
+
# caller-facing completion handle is also a TaskResult. Phronomy does not distinguish
|
|
18
|
+
# whether the reason is blocking I/O, CPU-bound work, or another long
|
|
19
|
+
# synchronous operation.
|
|
20
|
+
#
|
|
21
|
+
# @api private
|
|
22
|
+
module ToolExecutor
|
|
23
|
+
# @api private
|
|
24
|
+
def self.call_async(
|
|
25
|
+
tool:,
|
|
26
|
+
args:,
|
|
27
|
+
cancellation_token: nil,
|
|
28
|
+
config: {},
|
|
29
|
+
runtime: nil,
|
|
30
|
+
on_full: :raise
|
|
31
|
+
)
|
|
32
|
+
mode = tool.class.execution_mode
|
|
33
|
+
|
|
34
|
+
case mode
|
|
35
|
+
when :cooperative
|
|
36
|
+
task = Phronomy::TaskResult.deferred(name: "tool-#{tool.name}")
|
|
37
|
+
begin
|
|
38
|
+
task.complete(
|
|
39
|
+
tool.call(args, cancellation_token: cancellation_token)
|
|
40
|
+
)
|
|
41
|
+
rescue => error
|
|
42
|
+
task.fail(error)
|
|
43
|
+
end
|
|
44
|
+
task
|
|
45
|
+
when :offloaded
|
|
46
|
+
runtime ||= Phronomy::Runtime.instance
|
|
47
|
+
runtime.offload.submit(
|
|
48
|
+
cancellation_token: cancellation_token,
|
|
49
|
+
on_full: on_full
|
|
50
|
+
) do
|
|
51
|
+
tool.call(args, cancellation_token: cancellation_token)
|
|
52
|
+
end
|
|
53
|
+
else
|
|
54
|
+
raise Phronomy::ConfigurationError,
|
|
55
|
+
"unknown Tool execution_mode: #{mode.inspect}"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
module Agent
|
|
5
5
|
class ContextAssembler
|
|
6
|
-
ASSEMBLY_POLICY_VERSION =
|
|
6
|
+
ASSEMBLY_POLICY_VERSION = 9
|
|
7
7
|
SEGMENT_ORIGIN_METADATA_KEY = "phronomy_origin"
|
|
8
8
|
POLICY_ORIGIN_METADATA_KEY = "context_policy_origin"
|
|
9
9
|
POLICY_ITEM_ID_METADATA_KEY = "context_policy_item_id"
|
|
@@ -59,81 +59,33 @@ module Phronomy
|
|
|
59
59
|
excluded = [execution.metadata["current_input_record_id"]].compact
|
|
60
60
|
handoff_context = config[:phronomy_handoff_context]
|
|
61
61
|
|
|
62
|
-
instructions =
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
role: :system,
|
|
68
|
-
content: system_text.to_s,
|
|
69
|
-
content_format: :text,
|
|
70
|
-
estimated_tokens: estimate_value(system_text.to_s),
|
|
71
|
-
required: true,
|
|
72
|
-
provenance: ContextPolicyInput::Provenance.new(origin: :agent_configuration),
|
|
73
|
-
metadata: {}
|
|
74
|
-
)
|
|
75
|
-
end
|
|
76
|
-
if handoff_context
|
|
77
|
-
instructions << ContextPolicyInput::InstructionItem.new(
|
|
78
|
-
id: "instruction:handoff:#{execution.execution_id}:1",
|
|
79
|
-
kind: :handoff_responsibility,
|
|
80
|
-
role: :user,
|
|
81
|
-
content: handoff_context.responsibility.to_s,
|
|
82
|
-
content_format: :text,
|
|
83
|
-
estimated_tokens: estimate_value(handoff_context.responsibility.to_s),
|
|
84
|
-
required: true,
|
|
85
|
-
provenance: ContextPolicyInput::Provenance.new(origin: :handoff_context),
|
|
86
|
-
metadata: {SEGMENT_ORIGIN_METADATA_KEY => HANDOFF_CONTEXT_ORIGIN}
|
|
87
|
-
)
|
|
88
|
-
end
|
|
62
|
+
instructions = initial_instruction_items(
|
|
63
|
+
system_text,
|
|
64
|
+
handoff_context,
|
|
65
|
+
execution: execution
|
|
66
|
+
)
|
|
89
67
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
candidates = @candidate_resolver.resolve(
|
|
95
|
-
prior_records: projection.context_records,
|
|
96
|
-
working_records: eligible_working,
|
|
68
|
+
candidates = resolve_record_candidates(
|
|
69
|
+
projection,
|
|
70
|
+
agent_root: agent_root,
|
|
71
|
+
execution: execution,
|
|
97
72
|
excluded_record_ids: excluded
|
|
98
73
|
)
|
|
99
|
-
candidates =
|
|
74
|
+
candidates = merge_context_candidates(
|
|
100
75
|
candidates,
|
|
101
76
|
hook_candidates,
|
|
77
|
+
handoff_context,
|
|
102
78
|
agent_root: agent_root,
|
|
103
79
|
execution: execution,
|
|
104
80
|
call_sequence: 1
|
|
105
81
|
)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
82
|
+
current_input = current_input_item(
|
|
83
|
+
current_input_content,
|
|
84
|
+
input_ref: input_ref,
|
|
85
|
+
candidates: candidates,
|
|
86
|
+
agent_root: agent_root,
|
|
109
87
|
execution: execution
|
|
110
88
|
)
|
|
111
|
-
next_sequence = Array(candidates).filter_map(&:sequence).max.to_i + 1
|
|
112
|
-
current_input = ContextPolicyInput::ConversationItem.new(
|
|
113
|
-
id: "current-input:#{execution.execution_id}",
|
|
114
|
-
kind: :current_input,
|
|
115
|
-
role: :user,
|
|
116
|
-
content: current_input_content,
|
|
117
|
-
content_format: :text,
|
|
118
|
-
sequence: next_sequence,
|
|
119
|
-
estimated_tokens: estimate_value(current_input_content),
|
|
120
|
-
required: true,
|
|
121
|
-
provenance: ContextPolicyInput::Provenance.new(
|
|
122
|
-
origin: :working,
|
|
123
|
-
content_ref: input_ref,
|
|
124
|
-
record_id: execution.metadata["current_input_record_id"],
|
|
125
|
-
agent_id: agent_root.agent_id,
|
|
126
|
-
execution_id: execution.execution_id
|
|
127
|
-
),
|
|
128
|
-
tool_call_id: nil,
|
|
129
|
-
tool_call_ids: [],
|
|
130
|
-
delivery: :ask_argument,
|
|
131
|
-
metadata: {
|
|
132
|
-
"source_agent_id" => agent_root.agent_id,
|
|
133
|
-
"source_execution_id" => execution.execution_id,
|
|
134
|
-
"handoff_policy_category" => "current_request"
|
|
135
|
-
}
|
|
136
|
-
)
|
|
137
89
|
|
|
138
90
|
prepare(
|
|
139
91
|
agent_root: agent_root,
|
|
@@ -168,33 +120,23 @@ module Phronomy
|
|
|
168
120
|
additional_tools: handoff_tool_classes(config)
|
|
169
121
|
)
|
|
170
122
|
|
|
171
|
-
instructions = base_manifest
|
|
172
|
-
next unless retained_base_instruction?(segment)
|
|
173
|
-
instruction_item_from_manifest(segment)
|
|
174
|
-
end
|
|
123
|
+
instructions = retained_instruction_items(base_manifest)
|
|
175
124
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
candidates = @candidate_resolver.resolve(
|
|
181
|
-
prior_records: projection.context_records,
|
|
182
|
-
working_records: eligible_working,
|
|
125
|
+
candidates = resolve_record_candidates(
|
|
126
|
+
projection,
|
|
127
|
+
agent_root: agent_root,
|
|
128
|
+
execution: execution,
|
|
183
129
|
excluded_record_ids: []
|
|
184
130
|
)
|
|
185
131
|
call_sequence = execution.llm_calls.length + 1
|
|
186
|
-
candidates =
|
|
132
|
+
candidates = merge_context_candidates(
|
|
187
133
|
candidates,
|
|
188
134
|
hook_candidates,
|
|
135
|
+
handoff_context,
|
|
189
136
|
agent_root: agent_root,
|
|
190
137
|
execution: execution,
|
|
191
138
|
call_sequence: call_sequence
|
|
192
139
|
)
|
|
193
|
-
candidates = merge_handoff_candidates(
|
|
194
|
-
candidates,
|
|
195
|
-
handoff_context,
|
|
196
|
-
execution: execution
|
|
197
|
-
)
|
|
198
140
|
|
|
199
141
|
prepare(
|
|
200
142
|
agent_root: agent_root,
|
|
@@ -274,6 +216,111 @@ module Phronomy
|
|
|
274
216
|
|
|
275
217
|
private
|
|
276
218
|
|
|
219
|
+
def initial_instruction_items(system_text, handoff_context, execution:)
|
|
220
|
+
instructions = []
|
|
221
|
+
unless system_text.to_s.empty?
|
|
222
|
+
instructions << agent_instruction_item(system_text, execution: execution)
|
|
223
|
+
end
|
|
224
|
+
if handoff_context
|
|
225
|
+
instructions << handoff_instruction_item(handoff_context, execution: execution)
|
|
226
|
+
end
|
|
227
|
+
instructions
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def agent_instruction_item(system_text, execution:)
|
|
231
|
+
ContextPolicyInput::InstructionItem.new(
|
|
232
|
+
id: "instruction:agent:#{execution.execution_id}:1",
|
|
233
|
+
kind: :instruction,
|
|
234
|
+
role: :system,
|
|
235
|
+
content: system_text.to_s,
|
|
236
|
+
content_format: :text,
|
|
237
|
+
estimated_tokens: estimate_value(system_text.to_s),
|
|
238
|
+
required: true,
|
|
239
|
+
provenance: ContextPolicyInput::Provenance.new(origin: :agent_configuration),
|
|
240
|
+
metadata: {}
|
|
241
|
+
)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def handoff_instruction_item(handoff_context, execution:)
|
|
245
|
+
ContextPolicyInput::InstructionItem.new(
|
|
246
|
+
id: "instruction:handoff:#{execution.execution_id}:1",
|
|
247
|
+
kind: :handoff_responsibility,
|
|
248
|
+
role: :user,
|
|
249
|
+
content: handoff_context.responsibility.to_s,
|
|
250
|
+
content_format: :text,
|
|
251
|
+
estimated_tokens: estimate_value(handoff_context.responsibility.to_s),
|
|
252
|
+
required: true,
|
|
253
|
+
provenance: ContextPolicyInput::Provenance.new(origin: :handoff_context),
|
|
254
|
+
metadata: {SEGMENT_ORIGIN_METADATA_KEY => HANDOFF_CONTEXT_ORIGIN}
|
|
255
|
+
)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def retained_instruction_items(base_manifest)
|
|
259
|
+
base_manifest.segments.filter_map do |segment|
|
|
260
|
+
next unless retained_base_instruction?(segment)
|
|
261
|
+
instruction_item_from_manifest(segment)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def resolve_record_candidates(projection, agent_root:, execution:, excluded_record_ids:)
|
|
266
|
+
generation = agent_root.transcript_generation
|
|
267
|
+
eligible_working = Array(execution.working_records).select do |record|
|
|
268
|
+
record.context_candidate && record.context_generation == generation
|
|
269
|
+
end
|
|
270
|
+
@candidate_resolver.resolve(
|
|
271
|
+
prior_records: projection.context_records,
|
|
272
|
+
working_records: eligible_working,
|
|
273
|
+
excluded_record_ids: excluded_record_ids
|
|
274
|
+
)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def merge_context_candidates(
|
|
278
|
+
candidates, hook_candidates, handoff_context,
|
|
279
|
+
agent_root:, execution:, call_sequence:
|
|
280
|
+
)
|
|
281
|
+
candidates = merge_hook_candidates(
|
|
282
|
+
candidates,
|
|
283
|
+
hook_candidates,
|
|
284
|
+
agent_root: agent_root,
|
|
285
|
+
execution: execution,
|
|
286
|
+
call_sequence: call_sequence
|
|
287
|
+
)
|
|
288
|
+
merge_handoff_candidates(
|
|
289
|
+
candidates,
|
|
290
|
+
handoff_context,
|
|
291
|
+
execution: execution
|
|
292
|
+
)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def current_input_item(content, input_ref:, candidates:, agent_root:, execution:)
|
|
296
|
+
next_sequence = Array(candidates).filter_map(&:sequence).max.to_i + 1
|
|
297
|
+
ContextPolicyInput::ConversationItem.new(
|
|
298
|
+
id: "current-input:#{execution.execution_id}",
|
|
299
|
+
kind: :current_input,
|
|
300
|
+
role: :user,
|
|
301
|
+
content: content,
|
|
302
|
+
content_format: :text,
|
|
303
|
+
sequence: next_sequence,
|
|
304
|
+
estimated_tokens: estimate_value(content),
|
|
305
|
+
required: true,
|
|
306
|
+
provenance: ContextPolicyInput::Provenance.new(
|
|
307
|
+
origin: :working,
|
|
308
|
+
content_ref: input_ref,
|
|
309
|
+
record_id: execution.metadata["current_input_record_id"],
|
|
310
|
+
agent_id: agent_root.agent_id,
|
|
311
|
+
execution_id: execution.execution_id
|
|
312
|
+
),
|
|
313
|
+
tool_call_id: nil,
|
|
314
|
+
tool_call_ids: [],
|
|
315
|
+
delivery: :ask_argument,
|
|
316
|
+
metadata: {
|
|
317
|
+
"source_agent_id" => agent_root.agent_id,
|
|
318
|
+
"source_execution_id" => execution.execution_id,
|
|
319
|
+
"handoff_policy_category" => "current_request"
|
|
320
|
+
}
|
|
321
|
+
)
|
|
322
|
+
end
|
|
323
|
+
|
|
277
324
|
def prepare(
|
|
278
325
|
agent_root:,
|
|
279
326
|
execution:,
|
|
@@ -286,7 +333,7 @@ module Phronomy
|
|
|
286
333
|
tools:,
|
|
287
334
|
current_input:
|
|
288
335
|
)
|
|
289
|
-
token_budget = TokenBudgetResolver.new
|
|
336
|
+
token_budget = TokenBudgetResolver.new.resolve(model_config)
|
|
290
337
|
policy_input = @input_builder.build(
|
|
291
338
|
agent_id: agent_root.agent_id,
|
|
292
339
|
execution_id: execution.execution_id,
|
|
@@ -438,7 +485,7 @@ module Phronomy
|
|
|
438
485
|
|
|
439
486
|
def sanitized_item_metadata(item)
|
|
440
487
|
raw = item.metadata.to_h.transform_keys(&:to_s)
|
|
441
|
-
# :working origin is Phronomy-controlled (from
|
|
488
|
+
# :working origin is Phronomy-controlled (from InitialPreparation),
|
|
442
489
|
# so handoff routing metadata on working records is equally trusted.
|
|
443
490
|
trusted_handoff = if item.provenance.origin == :handoff || item.provenance.origin == :working
|
|
444
491
|
raw.slice(*TRUSTED_HANDOFF_METADATA_KEYS)
|
|
@@ -474,15 +521,23 @@ module Phronomy
|
|
|
474
521
|
"provider" => @agent.class.provider&.to_s,
|
|
475
522
|
"temperature" => @agent.class.temperature,
|
|
476
523
|
"max_output_tokens" => @agent.class.max_output_tokens,
|
|
477
|
-
"
|
|
478
|
-
"cache_instructions" => !!@agent.class.cache_instructions,
|
|
479
|
-
"parallel_tool_execution" => !!Phronomy.configuration.parallel_tool_execution
|
|
524
|
+
"cache_instructions" => !!@agent.class.cache_instructions
|
|
480
525
|
}.compact
|
|
481
526
|
end
|
|
482
527
|
|
|
483
528
|
def apply_model_config_patch(base, patch)
|
|
484
529
|
return base unless patch
|
|
485
|
-
|
|
530
|
+
values = patch.to_h.transform_keys(&:to_s)
|
|
531
|
+
if values.key?("context_window")
|
|
532
|
+
raise Phronomy::ConfigurationError,
|
|
533
|
+
"context_window is model metadata; configure the RubyLLM model registry"
|
|
534
|
+
end
|
|
535
|
+
if values.key?("max_output_tokens") && !values["max_output_tokens"].nil?
|
|
536
|
+
cap = Integer(values["max_output_tokens"])
|
|
537
|
+
raise ArgumentError, "max_output_tokens must be positive" unless cap.positive?
|
|
538
|
+
values["max_output_tokens"] = cap
|
|
539
|
+
end
|
|
540
|
+
base.merge(values).compact
|
|
486
541
|
end
|
|
487
542
|
|
|
488
543
|
def normalize_candidates(candidates)
|
|
@@ -8,8 +8,8 @@ module Phronomy
|
|
|
8
8
|
) do
|
|
9
9
|
def initialize(**values)
|
|
10
10
|
super(**values.merge(
|
|
11
|
-
content: Immutable.copy(values[:content]),
|
|
12
|
-
metadata: Immutable.copy(values[:metadata] || {})
|
|
11
|
+
content: Phronomy::Values::Immutable.copy(values[:content]),
|
|
12
|
+
metadata: Phronomy::Values::Immutable.copy(values[:metadata] || {})
|
|
13
13
|
))
|
|
14
14
|
freeze
|
|
15
15
|
end
|
data/lib/phronomy/agent/{ruby_llm_materializer.rb → context_assembly/ruby_llm_materializer.rb}
RENAMED
|
@@ -43,7 +43,7 @@ module Phronomy
|
|
|
43
43
|
messages: materialize_message_segments(message_segments).freeze,
|
|
44
44
|
tool_classes: tool_set.runtime_tools,
|
|
45
45
|
ask_message: ask_message,
|
|
46
|
-
model_config: Immutable.copy(model_config),
|
|
46
|
+
model_config: Phronomy::Values::Immutable.copy(model_config),
|
|
47
47
|
manifest: manifest,
|
|
48
48
|
manifest_ref: manifest_ref
|
|
49
49
|
)
|
|
@@ -151,22 +151,19 @@ module Phronomy
|
|
|
151
151
|
end
|
|
152
152
|
|
|
153
153
|
tool_calls = materialize_tool_calls(payload["tool_calls"])
|
|
154
|
-
|
|
154
|
+
RubyLLM::Message.new(
|
|
155
155
|
role: role,
|
|
156
156
|
content: initial_content_for(role, payload.fetch("content", nil), tool_calls),
|
|
157
157
|
tool_calls: tool_calls.empty? ? nil : tool_calls,
|
|
158
158
|
tool_call_id: payload["tool_call_id"],
|
|
159
|
-
|
|
159
|
+
model: payload["model_id"]
|
|
160
160
|
)
|
|
161
|
-
|
|
162
|
-
message.content = payload["content"] if payload.key?("content")
|
|
163
|
-
message
|
|
164
161
|
end
|
|
165
162
|
|
|
166
163
|
def initial_content_for(role, content, tool_calls)
|
|
167
164
|
return "" if role == :assistant && content.nil? && !tool_calls.empty?
|
|
168
165
|
|
|
169
|
-
content.
|
|
166
|
+
(content.is_a?(Hash) || content.is_a?(Array)) ? JSON.generate(content) : content
|
|
170
167
|
end
|
|
171
168
|
|
|
172
169
|
def materialize_tool_calls(payloads)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Constructs provider chat objects from already selected model settings.
|
|
6
|
+
# Agent hooks retain projection installation and invocation-specific Tools.
|
|
7
|
+
# @api private
|
|
8
|
+
class RuntimeChatBuilder
|
|
9
|
+
def self.build(config)
|
|
10
|
+
chat = RubyLLM.chat(**chat_options(config))
|
|
11
|
+
chat.with_temperature(config["temperature"]) if config["temperature"]
|
|
12
|
+
if config["max_output_tokens"]
|
|
13
|
+
chat.with_max_output_tokens(config["max_output_tokens"])
|
|
14
|
+
end
|
|
15
|
+
chat
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.apply_instructions(chat, text, cache:, provider:)
|
|
19
|
+
chat.with_instructions(text, cache_until_here: cache)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.chat_options(config)
|
|
23
|
+
opts = {}
|
|
24
|
+
model = config["model"]
|
|
25
|
+
opts[:model] = model if model
|
|
26
|
+
provider = config["provider"]
|
|
27
|
+
if provider
|
|
28
|
+
opts[:provider] = provider.to_sym
|
|
29
|
+
opts[:assume_model_exists] = true
|
|
30
|
+
end
|
|
31
|
+
opts
|
|
32
|
+
end
|
|
33
|
+
private_class_method :chat_options
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|